@nanoporetech-digital/components 3.4.0 → 3.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +12 -0
- package/dist/cjs/index-41582c2a.js +16 -12
- package/dist/cjs/index.cjs.js.map +1 -1
- package/dist/cjs/loader.cjs.js +1 -1
- package/dist/cjs/nano-components.cjs.js +1 -1
- package/dist/cjs/nano-demo.cjs.entry.js +291 -0
- package/dist/cjs/nano-demo.cjs.entry.js.map +1 -0
- package/dist/cjs/{nano-table-b9cdafab.js → nano-table-83e46f68.js} +486 -202
- package/dist/cjs/nano-table-83e46f68.js.map +1 -0
- package/dist/cjs/nano-table.cjs.entry.js +1 -1
- package/dist/cjs/{table.worker-291904c9.js → table.worker-525ec230.js} +3 -3
- package/dist/cjs/table.worker-525ec230.js.map +1 -0
- package/dist/cjs/table.worker-e9fb087e.js +4 -0
- package/dist/collection/collection-manifest.json +1 -0
- package/dist/collection/components/alert/alert.helpers.js.map +1 -1
- package/dist/collection/components/demo/demo.js +284 -0
- package/dist/collection/components/demo/demo.js.map +1 -0
- package/dist/collection/components/dialog/dialog.helpers.js.map +1 -1
- package/dist/collection/components/table/table-interface.js.map +1 -1
- package/dist/collection/components/table/table.cell.js +65 -0
- package/dist/collection/components/table/table.cell.js.map +1 -0
- package/dist/collection/components/table/table.css +98 -20
- package/dist/collection/components/table/table.header.js +156 -0
- package/dist/collection/components/table/table.header.js.map +1 -0
- package/dist/collection/components/table/table.js +170 -18
- package/dist/collection/components/table/table.js.map +1 -1
- package/dist/collection/components/table/table.row.js +113 -0
- package/dist/collection/components/table/table.row.js.map +1 -0
- package/dist/collection/components/table/table.store.js +46 -9
- package/dist/collection/components/table/table.store.js.map +1 -1
- package/dist/collection/components/table/table.utils.js +230 -0
- package/dist/collection/components/table/table.utils.js.map +1 -1
- package/dist/collection/components/table/table.worker.js +1 -0
- package/dist/collection/components/table/table.worker.js.map +1 -1
- package/dist/components/index.d.ts +1 -0
- package/dist/components/index.js +1 -0
- package/dist/components/index.js.map +1 -1
- package/dist/components/nano-demo.d.ts +11 -0
- package/dist/components/nano-demo.js +341 -0
- package/dist/components/nano-demo.js.map +1 -0
- package/dist/components/nano-table.js +2 -1247
- package/dist/components/nano-table.js.map +1 -1
- package/dist/components/table.js +1539 -0
- package/dist/components/table.js.map +1 -0
- package/dist/components/table.worker.js +2 -2
- package/dist/custom-elements/index.d.ts +6 -0
- package/dist/custom-elements/index.js +772 -204
- package/dist/custom-elements/index.js.map +1 -1
- package/dist/esm/index-3c280603.js +16 -12
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/loader.js +1 -1
- package/dist/esm/nano-components.js +1 -1
- package/dist/esm/nano-demo.entry.js +287 -0
- package/dist/esm/nano-demo.entry.js.map +1 -0
- package/dist/esm/{nano-table-c8ef2276.js → nano-table-e2405350.js} +487 -203
- package/dist/esm/nano-table-e2405350.js.map +1 -0
- package/dist/esm/nano-table.entry.js +1 -1
- package/dist/esm/{table.worker-65438fa0.js → table.worker-739c193f.js} +3 -3
- package/dist/esm/table.worker-739c193f.js.map +1 -0
- package/dist/esm/table.worker-e9fb087e.js +4 -0
- package/dist/nano-components/index.esm.js.map +1 -1
- package/dist/nano-components/nano-components.esm.js +1 -1
- package/dist/nano-components/nano-components.esm.js.map +1 -1
- package/dist/nano-components/p-28fdfa6b.js +5 -0
- package/dist/nano-components/p-28fdfa6b.js.map +1 -0
- package/dist/nano-components/p-85cfb0af.entry.js +5 -0
- package/dist/nano-components/p-85cfb0af.entry.js.map +1 -0
- package/dist/nano-components/p-a71989f3.js +5 -0
- package/dist/nano-components/{p-14218d23.entry.js.map → p-a71989f3.js.map} +0 -0
- package/dist/nano-components/{p-14218d23.entry.js → p-e4a28360.entry.js} +2 -2
- package/dist/nano-components/{p-fe1f8360.js.map → p-e4a28360.entry.js.map} +0 -0
- package/dist/nano-components/p-e9fb087e.js +4 -0
- package/dist/types/components/alert/alert.helpers.d.ts +1 -1
- package/dist/types/components/demo/demo.d.ts +6 -0
- package/dist/types/components/dialog/dialog.helpers.d.ts +1 -1
- package/dist/types/components/table/table-interface.d.ts +38 -23
- package/dist/types/components/table/table.cell.d.ts +18 -0
- package/dist/types/components/table/table.d.ts +36 -1
- package/dist/types/components/table/table.header.d.ts +16 -0
- package/dist/types/components/table/table.row.d.ts +15 -0
- package/dist/types/components/table/table.utils.d.ts +99 -0
- package/dist/types/components.d.ts +50 -0
- package/docs-json.json +124 -3
- package/docs-vscode.json +17 -1
- package/package.json +2 -2
- package/dist/cjs/nano-table-b9cdafab.js.map +0 -1
- package/dist/cjs/table.worker-1fd13775.js +0 -4
- package/dist/cjs/table.worker-291904c9.js.map +0 -1
- package/dist/collection/components/table/table.children.js +0 -224
- package/dist/collection/components/table/table.children.js.map +0 -1
- package/dist/collection/components/table/table.service.js +0 -121
- package/dist/collection/components/table/table.service.js.map +0 -1
- package/dist/esm/nano-table-c8ef2276.js.map +0 -1
- package/dist/esm/table.worker-1fd13775.js +0 -4
- package/dist/esm/table.worker-65438fa0.js.map +0 -1
- package/dist/nano-components/p-1fd13775.js +0 -4
- package/dist/nano-components/p-f9349146.js +0 -5
- package/dist/nano-components/p-f9349146.js.map +0 -1
- package/dist/nano-components/p-fe1f8360.js +0 -5
- package/dist/types/components/table/table.children.d.ts +0 -30
- package/dist/types/components/table/table.service.d.ts +0 -42
@@ -0,0 +1,1539 @@
|
|
1
|
+
/*!
|
2
|
+
* Web Components for Nanopore digital Web Apps
|
3
|
+
*/
|
4
|
+
import { consoleError, getElement, getRenderingRef, h, Fragment, proxyCustomElement, HTMLElement, createEvent, readTask, Host, Build } from '@stencil/core/internal/client';
|
5
|
+
import { a as cyrb53 } from './math.js';
|
6
|
+
import { d as debounce } from './throttle.js';
|
7
|
+
import { c as createStore } from './index2.js';
|
8
|
+
import { d as defineCustomElement$5 } from './icon.js';
|
9
|
+
import { d as defineCustomElement$4 } from './progress-bar.js';
|
10
|
+
import { d as defineCustomElement$3 } from './resize-observe.js';
|
11
|
+
import { d as defineCustomElement$2 } from './skeleton.js';
|
12
|
+
import { d as defineCustomElement$1 } from './spinner.js';
|
13
|
+
|
14
|
+
const CSSNAMESPACE = 'nano-tbl';
|
15
|
+
|
16
|
+
const isInstanceOf = (value, className) => {
|
17
|
+
const C = globalThis[className];
|
18
|
+
return C != null && value instanceof C;
|
19
|
+
};
|
20
|
+
const getTransferables = (value) => {
|
21
|
+
if (value != null) {
|
22
|
+
if (
|
23
|
+
isInstanceOf(value, "ArrayBuffer") ||
|
24
|
+
isInstanceOf(value, "MessagePort") ||
|
25
|
+
isInstanceOf(value, "ImageBitmap") ||
|
26
|
+
isInstanceOf(value, "OffscreenCanvas")
|
27
|
+
) {
|
28
|
+
return [value];
|
29
|
+
}
|
30
|
+
if (typeof value === "object") {
|
31
|
+
if (value.constructor === Object) {
|
32
|
+
value = Object.values(value);
|
33
|
+
}
|
34
|
+
if (Array.isArray(value)) {
|
35
|
+
return value.flatMap(getTransferables);
|
36
|
+
}
|
37
|
+
return getTransferables(value.buffer);
|
38
|
+
}
|
39
|
+
}
|
40
|
+
return [];
|
41
|
+
};
|
42
|
+
|
43
|
+
let pendingIds = 0;
|
44
|
+
let callbackIds = 0;
|
45
|
+
const pending = new Map();
|
46
|
+
const callbacks = new Map();
|
47
|
+
|
48
|
+
const createWorker = (workerPath, workerName, workerMsgId) => {
|
49
|
+
const worker = new Worker(workerPath, {name:workerName});
|
50
|
+
|
51
|
+
worker.addEventListener('message', ({data}) => {
|
52
|
+
if (data) {
|
53
|
+
const workerMsg = data[0];
|
54
|
+
const id = data[1];
|
55
|
+
const value = data[2];
|
56
|
+
|
57
|
+
if (workerMsg === workerMsgId) {
|
58
|
+
const err = data[3];
|
59
|
+
const [resolve, reject, callbackIds] = pending.get(id);
|
60
|
+
pending.delete(id);
|
61
|
+
|
62
|
+
if (err) {
|
63
|
+
const errObj = (err.isError)
|
64
|
+
? Object.assign(new Error(err.value.message), err.value)
|
65
|
+
: err.value;
|
66
|
+
|
67
|
+
consoleError(errObj);
|
68
|
+
reject(errObj);
|
69
|
+
} else {
|
70
|
+
if (callbackIds) {
|
71
|
+
callbackIds.forEach(id => callbacks.delete(id));
|
72
|
+
}
|
73
|
+
resolve(value);
|
74
|
+
}
|
75
|
+
} else if (workerMsg === workerMsgId + '.cb') {
|
76
|
+
try {
|
77
|
+
callbacks.get(id)(...value);
|
78
|
+
} catch (e) {
|
79
|
+
consoleError(e);
|
80
|
+
}
|
81
|
+
}
|
82
|
+
}
|
83
|
+
});
|
84
|
+
|
85
|
+
return worker;
|
86
|
+
};
|
87
|
+
|
88
|
+
const createWorkerProxy = (worker, workerMsgId, exportedMethod) => (
|
89
|
+
(...args) => new Promise((resolve, reject) => {
|
90
|
+
let pendingId = pendingIds++;
|
91
|
+
let i = 0;
|
92
|
+
let argLen = args.length;
|
93
|
+
let mainData = [resolve, reject];
|
94
|
+
pending.set(pendingId, mainData);
|
95
|
+
|
96
|
+
for (; i < argLen; i++) {
|
97
|
+
if (typeof args[i] === 'function') {
|
98
|
+
const callbackId = callbackIds++;
|
99
|
+
callbacks.set(callbackId, args[i]);
|
100
|
+
args[i] = [workerMsgId + '.cb', callbackId];
|
101
|
+
(mainData[2] = mainData[2] || []).push(callbackId);
|
102
|
+
}
|
103
|
+
}
|
104
|
+
const postMessage = (w) => (
|
105
|
+
w.postMessage(
|
106
|
+
[workerMsgId, pendingId, exportedMethod, args],
|
107
|
+
getTransferables(args)
|
108
|
+
)
|
109
|
+
);
|
110
|
+
if (worker.then) {
|
111
|
+
worker.then(postMessage);
|
112
|
+
} else {
|
113
|
+
postMessage(worker);
|
114
|
+
}
|
115
|
+
})
|
116
|
+
);
|
117
|
+
|
118
|
+
const workerPromise = import('./table.worker.js').then(m => m.worker);
|
119
|
+
const createWorkerStore = /*@__PURE__*/createWorkerProxy(workerPromise, 'stencil.table.worker', 'createWorkerStore');
|
120
|
+
const syncConfigToWorker = /*@__PURE__*/createWorkerProxy(workerPromise, 'stencil.table.worker', 'syncConfigToWorker');
|
121
|
+
const syncDataToWorker = /*@__PURE__*/createWorkerProxy(workerPromise, 'stencil.table.worker', 'syncDataToWorker');
|
122
|
+
const workerFilter = /*@__PURE__*/createWorkerProxy(workerPromise, 'stencil.table.worker', 'workerFilter');
|
123
|
+
const workerSearch = /*@__PURE__*/createWorkerProxy(workerPromise, 'stencil.table.worker', 'workerSearch');
|
124
|
+
const workerSort = /*@__PURE__*/createWorkerProxy(workerPromise, 'stencil.table.worker', 'workerSort');
|
125
|
+
|
126
|
+
function colsToWorker(columns) {
|
127
|
+
const safeColumns = JSON.parse(JSON.stringify(columns));
|
128
|
+
columns.forEach((c) => {
|
129
|
+
if (!!c.sortCompareFn) {
|
130
|
+
const safeCol = safeColumns.find((sc) => sc.prop === c.prop);
|
131
|
+
safeCol.sortCompareFn = c.sortCompareFn.toString();
|
132
|
+
}
|
133
|
+
});
|
134
|
+
return safeColumns;
|
135
|
+
}
|
136
|
+
const stores = new WeakMap();
|
137
|
+
async function generateStore(host, columns, scrollParent) {
|
138
|
+
const store = {
|
139
|
+
data: createStore({ rows: [] }),
|
140
|
+
config: createStore({ columns }),
|
141
|
+
general: createStore({ workerId: null, scrollParent, host }),
|
142
|
+
};
|
143
|
+
const id = await createWorkerStore(store.data.state.rows, colsToWorker(store.config.state.columns));
|
144
|
+
store.general.state.workerId = id;
|
145
|
+
stores.set(host, store);
|
146
|
+
// sync data from our store to our worker
|
147
|
+
store.data.use({
|
148
|
+
reset: () => {
|
149
|
+
if (store.general.state.workerId)
|
150
|
+
syncDataToWorker(store.general.state.workerId, null);
|
151
|
+
},
|
152
|
+
dispose: () => {
|
153
|
+
if (store.general.state.workerId)
|
154
|
+
syncDataToWorker(store.general.state.workerId, null);
|
155
|
+
},
|
156
|
+
});
|
157
|
+
store.config.use({
|
158
|
+
reset: () => {
|
159
|
+
if (store.general.state.workerId)
|
160
|
+
syncConfigToWorker(store.general.state.workerId, null);
|
161
|
+
},
|
162
|
+
dispose: () => {
|
163
|
+
if (store.general.state.workerId)
|
164
|
+
syncConfigToWorker(store.general.state.workerId, null);
|
165
|
+
},
|
166
|
+
});
|
167
|
+
return store;
|
168
|
+
}
|
169
|
+
function getStore(host) {
|
170
|
+
return stores.get(host);
|
171
|
+
}
|
172
|
+
function getDataType(value) {
|
173
|
+
if (value instanceof Date) {
|
174
|
+
return 'date';
|
175
|
+
}
|
176
|
+
if (['number', 'string'].includes(typeof value)) {
|
177
|
+
return typeof value;
|
178
|
+
}
|
179
|
+
return 'unknown';
|
180
|
+
}
|
181
|
+
function storeSetData(host, rows) {
|
182
|
+
const store = stores.get(host);
|
183
|
+
if (!store)
|
184
|
+
return;
|
185
|
+
const cols = store.config.state.columns;
|
186
|
+
const dateCols = cols
|
187
|
+
.filter((col) => col.type === 'date')
|
188
|
+
.reduce((prev, curr) => {
|
189
|
+
return [...prev, curr.prop];
|
190
|
+
}, []);
|
191
|
+
const unknownCols = cols.filter((col) => !col.type);
|
192
|
+
// augments data with some internal.
|
193
|
+
// discern unknown column types
|
194
|
+
// convert dates to numbers for worker
|
195
|
+
rows = rows.map((row, i) => {
|
196
|
+
// try our best to discern the column type (from first row) if unset
|
197
|
+
if (unknownCols.length && i === 0) {
|
198
|
+
store.config.state.columns = cols.map((col) => {
|
199
|
+
if (unknownCols.includes(col) && row[col.prop] && !col.type) {
|
200
|
+
col.type = getDataType(row[col.prop]);
|
201
|
+
if (col.type === 'date')
|
202
|
+
dateCols.push(col.prop);
|
203
|
+
}
|
204
|
+
return col;
|
205
|
+
});
|
206
|
+
}
|
207
|
+
// convert date columns into numbers to send to our worker
|
208
|
+
dateCols.forEach((colName) => {
|
209
|
+
// coerce any date type;
|
210
|
+
// Date(), timestamp, valid date string
|
211
|
+
const coerceDate = new Date(row[colName]) ?? null;
|
212
|
+
if (!!coerceDate && Number(coerceDate))
|
213
|
+
row[colName] = Number(coerceDate);
|
214
|
+
});
|
215
|
+
row['__index'] = i;
|
216
|
+
row['__uuid'] = cyrb53(Object.values(row).join());
|
217
|
+
return row;
|
218
|
+
});
|
219
|
+
store.data.state.rows = rows;
|
220
|
+
if (store.general.state.workerId)
|
221
|
+
return syncDataToWorker(store.general.state.workerId, rows);
|
222
|
+
}
|
223
|
+
function storeSetConfig(host, columns) {
|
224
|
+
const store = stores.get(host);
|
225
|
+
if (!store)
|
226
|
+
return;
|
227
|
+
store.config.state.columns = columns;
|
228
|
+
if (store.general.state.workerId)
|
229
|
+
return syncConfigToWorker(store.general.state.workerId, colsToWorker(columns));
|
230
|
+
}
|
231
|
+
async function storeSearch(host, term) {
|
232
|
+
const store = stores.get(host);
|
233
|
+
if (!store || !store.general.state.workerId)
|
234
|
+
return;
|
235
|
+
try {
|
236
|
+
store.data.state.rows = await workerSearch(store.general.state.workerId, term);
|
237
|
+
}
|
238
|
+
catch (e) {
|
239
|
+
console.warn(e);
|
240
|
+
}
|
241
|
+
}
|
242
|
+
async function storeFilter(host, filters) {
|
243
|
+
const store = stores.get(host);
|
244
|
+
if (!store || !store.general.state.workerId)
|
245
|
+
return;
|
246
|
+
try {
|
247
|
+
store.data.state.rows = await workerFilter(store.general.state.workerId, filters);
|
248
|
+
}
|
249
|
+
catch (e) {
|
250
|
+
console.warn(e);
|
251
|
+
}
|
252
|
+
}
|
253
|
+
async function storeSort(host, prop, order) {
|
254
|
+
const store = stores.get(host);
|
255
|
+
if (!store || !store.general.state.workerId)
|
256
|
+
return;
|
257
|
+
try {
|
258
|
+
store.data.state.rows = await workerSort(store.general.state.workerId, prop, order);
|
259
|
+
}
|
260
|
+
catch (e) {
|
261
|
+
console.warn(e);
|
262
|
+
}
|
263
|
+
}
|
264
|
+
|
265
|
+
/**
|
266
|
+
* Get a model object for custom cell / property renderers.
|
267
|
+
* @param rowIndex - the current row index being rendered
|
268
|
+
* @param colIndex - the current column index being rendered
|
269
|
+
* @returns a model object which will be passed to custom renderers
|
270
|
+
*/
|
271
|
+
function colDataModel(rowIndex, colIndex) {
|
272
|
+
const store = fetchStores();
|
273
|
+
const columns = store.config.state.columns;
|
274
|
+
const rows = store.data.state.rows;
|
275
|
+
const column = columns[colIndex];
|
276
|
+
const prop = column?.prop;
|
277
|
+
const rowModel = rows[rowIndex];
|
278
|
+
const cellModel = rowModel[columns[colIndex].prop];
|
279
|
+
return {
|
280
|
+
prop,
|
281
|
+
cellModel,
|
282
|
+
column,
|
283
|
+
rowIndex,
|
284
|
+
rowModel,
|
285
|
+
};
|
286
|
+
}
|
287
|
+
/**
|
288
|
+
* Get a model object for custom row renderers.
|
289
|
+
* @param rowIndex
|
290
|
+
* @returns a model object passed to custom row renderers
|
291
|
+
*/
|
292
|
+
function rowDataModel(rowIndex) {
|
293
|
+
const store = fetchStores();
|
294
|
+
const rows = store.data.state.rows;
|
295
|
+
const row = rows[rowIndex];
|
296
|
+
return {
|
297
|
+
row,
|
298
|
+
rowIndex,
|
299
|
+
};
|
300
|
+
}
|
301
|
+
/**
|
302
|
+
* Merges 2 objects of properties together
|
303
|
+
* @param current - property object
|
304
|
+
* @param extra - additional object property
|
305
|
+
* @returns - merged properties that can be applied to a node
|
306
|
+
*/
|
307
|
+
function mergeProperties(current, extra) {
|
308
|
+
if (!extra)
|
309
|
+
return current;
|
310
|
+
// top level merge
|
311
|
+
const props = { ...extra, ...current };
|
312
|
+
// deeper merge
|
313
|
+
// merge classes maps or strings
|
314
|
+
if (extra.class) {
|
315
|
+
if (typeof extra.class === 'object' && typeof props.class === 'object') {
|
316
|
+
props.class = { ...extra.class, ...props.class };
|
317
|
+
}
|
318
|
+
else if (typeof extra.class === 'string' &&
|
319
|
+
typeof props.class === 'object') {
|
320
|
+
props.class[extra.class] = true;
|
321
|
+
}
|
322
|
+
else if (typeof props.class === 'string') {
|
323
|
+
props.class += ' ' + extra.class;
|
324
|
+
}
|
325
|
+
}
|
326
|
+
// merge style
|
327
|
+
if (extra.style) {
|
328
|
+
props.style = { ...extra.style, ...props.style };
|
329
|
+
}
|
330
|
+
return props;
|
331
|
+
}
|
332
|
+
/**
|
333
|
+
* Returns the current nano-table's stores.
|
334
|
+
* @returns the current nano-table stores
|
335
|
+
*/
|
336
|
+
function fetchStores() {
|
337
|
+
return getStore(getElement(getRenderingRef()));
|
338
|
+
}
|
339
|
+
/**
|
340
|
+
* Merges any defined cell properties with properties
|
341
|
+
* required by `nano-table` functionality
|
342
|
+
* @param rowIndex - the current row index being rendered
|
343
|
+
* @param colIndex = the current column index being rendered
|
344
|
+
* @param defaultProps - default properties required by `nano-table`
|
345
|
+
* @returns - the merged properties that will be applied to a node
|
346
|
+
*/
|
347
|
+
function mergeCellProperties(rowIndex, colIndex, defaultProps) {
|
348
|
+
const props = { ...defaultProps };
|
349
|
+
const extraPropsFunc = fetchStores().config.state.columns[colIndex]?.cellProperties;
|
350
|
+
if (!extraPropsFunc)
|
351
|
+
return props;
|
352
|
+
const data = colDataModel(rowIndex, colIndex);
|
353
|
+
const extra = extraPropsFunc(data);
|
354
|
+
if (!extra)
|
355
|
+
return props;
|
356
|
+
return mergeProperties(props, extra);
|
357
|
+
}
|
358
|
+
/**
|
359
|
+
* Renders a table header (within a thead) using a custom template if set.
|
360
|
+
* @param col - the current column config object
|
361
|
+
* @returns - a JSX node
|
362
|
+
*/
|
363
|
+
function colheadFootRender(col) {
|
364
|
+
const tpl = col?.colTemplate;
|
365
|
+
return tpl ? (tpl(h, col)) : (h(Fragment, null, col.title));
|
366
|
+
}
|
367
|
+
const stickyHIOs = new WeakMap();
|
368
|
+
const stickyVIOs = new WeakMap();
|
369
|
+
/**
|
370
|
+
* Adds element to Intersection Observer. Fires when element changes on the x axis
|
371
|
+
* @param el - an element to observe
|
372
|
+
* @param pos - the edge to watch (start or end)
|
373
|
+
* @param cb - callback when an intersection state changes.
|
374
|
+
*/
|
375
|
+
function addHObserver(el, pos, cb) {
|
376
|
+
if (stickyHIOs.get(el))
|
377
|
+
return;
|
378
|
+
const store = fetchStores();
|
379
|
+
const root = store.general.state.scrollParent;
|
380
|
+
const observer = new IntersectionObserver(([e]) => {
|
381
|
+
const rootBounds = e.rootBounds || document.scrollingElement.getBoundingClientRect();
|
382
|
+
const positions = {};
|
383
|
+
if (pos === 'start') {
|
384
|
+
positions.start =
|
385
|
+
e.boundingClientRect.x - (rootBounds.x + root.scrollLeft) < 0 &&
|
386
|
+
!e.isIntersecting;
|
387
|
+
}
|
388
|
+
if (pos === 'end') {
|
389
|
+
// TODO - sort these out for RtL
|
390
|
+
positions.end =
|
391
|
+
e.boundingClientRect.right > e.boundingClientRect.width &&
|
392
|
+
!e.isIntersecting;
|
393
|
+
}
|
394
|
+
if (!!cb)
|
395
|
+
cb(positions);
|
396
|
+
}, {
|
397
|
+
threshold: [1],
|
398
|
+
rootMargin: '1px 0px 100px 0px',
|
399
|
+
root: root === document.scrollingElement ? null : root,
|
400
|
+
});
|
401
|
+
stickyHIOs.set(el, observer);
|
402
|
+
// dirty fix - wait a tick 'cos nano-size-observer isn't always ready in-time
|
403
|
+
setTimeout(() => observer.observe(el), 300);
|
404
|
+
}
|
405
|
+
/**
|
406
|
+
* Adds element to Intersection Observer. Fires when element changes on the y axis
|
407
|
+
* @param el - an element to observe
|
408
|
+
* @param pos - the edge to watch (start or end)
|
409
|
+
* @param cb - callback when an intersection state changes.
|
410
|
+
*/
|
411
|
+
function addVObserver(el, pos, cb) {
|
412
|
+
if (stickyVIOs.get(el))
|
413
|
+
return;
|
414
|
+
const store = fetchStores();
|
415
|
+
const root = store.general.state.scrollParent;
|
416
|
+
const observer = new IntersectionObserver(([e]) => {
|
417
|
+
const rootBounds = e.rootBounds || document.scrollingElement.getBoundingClientRect();
|
418
|
+
const positions = {};
|
419
|
+
if (pos === 'top') {
|
420
|
+
positions.top =
|
421
|
+
e.boundingClientRect.y - (rootBounds.y + root.scrollTop) < 0 &&
|
422
|
+
!e.isIntersecting;
|
423
|
+
}
|
424
|
+
if (pos === 'bottom') {
|
425
|
+
const boundingClientRect = e.target.getBoundingClientRect();
|
426
|
+
positions.bottom =
|
427
|
+
boundingClientRect.height + boundingClientRect.y >
|
428
|
+
rootBounds.height && !e.isIntersecting;
|
429
|
+
}
|
430
|
+
if (!!cb)
|
431
|
+
cb(positions);
|
432
|
+
}, {
|
433
|
+
threshold: [1],
|
434
|
+
rootMargin: '0px 100px 0px 100px',
|
435
|
+
root: root === document.scrollingElement ? null : root,
|
436
|
+
});
|
437
|
+
stickyVIOs.set(el, observer);
|
438
|
+
requestAnimationFrame(() => observer.observe(el));
|
439
|
+
}
|
440
|
+
function headerPinClasses(type, vPinned, toString = false) {
|
441
|
+
const classes = {
|
442
|
+
[`${CSSNAMESPACE}__${type}`]: true,
|
443
|
+
[`${CSSNAMESPACE}__pin`]: !!vPinned,
|
444
|
+
[`${CSSNAMESPACE}__pin--top`]: vPinned === 'top',
|
445
|
+
[`${CSSNAMESPACE}__pin--bottom`]: vPinned === 'bottom',
|
446
|
+
};
|
447
|
+
if (toString)
|
448
|
+
return classListToStr(classes);
|
449
|
+
return classes;
|
450
|
+
}
|
451
|
+
/**
|
452
|
+
* Turns a class map {'string': boolean} to class string
|
453
|
+
* @param classes - the class map to convert
|
454
|
+
* @returns a class string
|
455
|
+
*/
|
456
|
+
function classListToStr(classes) {
|
457
|
+
let classString = '';
|
458
|
+
Object.entries(classes).forEach(([className, on]) => {
|
459
|
+
if (on)
|
460
|
+
classString += className + ' ';
|
461
|
+
});
|
462
|
+
return classString;
|
463
|
+
}
|
464
|
+
/**
|
465
|
+
* Detects the current scroll speed as a number.
|
466
|
+
* Use within a scroll listener
|
467
|
+
*/
|
468
|
+
const detectScrollSpeed = (() => {
|
469
|
+
let lastPos;
|
470
|
+
let newPos;
|
471
|
+
let timer;
|
472
|
+
let delta;
|
473
|
+
const delay = 60; // in "ms" (higher means lower fidelity )
|
474
|
+
const clear = () => {
|
475
|
+
lastPos = null;
|
476
|
+
delta = 0;
|
477
|
+
};
|
478
|
+
clear();
|
479
|
+
return () => {
|
480
|
+
newPos = window.scrollY;
|
481
|
+
if (lastPos != null)
|
482
|
+
delta = newPos - lastPos;
|
483
|
+
lastPos = newPos;
|
484
|
+
window.clearTimeout(timer);
|
485
|
+
timer = window.setTimeout(clear, delay);
|
486
|
+
return delta;
|
487
|
+
};
|
488
|
+
})();
|
489
|
+
/**
|
490
|
+
* Attempts to find the closes scrolling parental element
|
491
|
+
* @param element - the element from which to traverse up the DOM
|
492
|
+
* @returns - the closest scrolling parental element
|
493
|
+
*/
|
494
|
+
function findScrollParent(element) {
|
495
|
+
let style = getComputedStyle(element);
|
496
|
+
const excludeStaticParent = style.position === 'absolute';
|
497
|
+
const overflowRegex = /(auto|scroll)/;
|
498
|
+
if (style.position === 'fixed')
|
499
|
+
return document.documentElement;
|
500
|
+
for (let parent = element; (parent = parent.parentElement);) {
|
501
|
+
style = getComputedStyle(parent);
|
502
|
+
if (excludeStaticParent && style.position === 'static') {
|
503
|
+
continue;
|
504
|
+
}
|
505
|
+
if (overflowRegex.test(style.overflow + style.overflowY + style.overflowX))
|
506
|
+
return parent;
|
507
|
+
}
|
508
|
+
return document.documentElement;
|
509
|
+
}
|
510
|
+
/**
|
511
|
+
* Checks whether an element is currently viewable within the viewport
|
512
|
+
* @param el - element to check
|
513
|
+
* @param percentVisible - the percentage of the element that should be within the viewport
|
514
|
+
* @returns true if the element's area percentage is visible
|
515
|
+
*/
|
516
|
+
function isInViewport(el, percentVisible = 100) {
|
517
|
+
const r = el.getBoundingClientRect();
|
518
|
+
const windowHeight = window.innerHeight || document.documentElement.clientHeight;
|
519
|
+
if (!r.bottom &&
|
520
|
+
!r.top &&
|
521
|
+
!r.left &&
|
522
|
+
!r.right &&
|
523
|
+
!r.height &&
|
524
|
+
!r.width &&
|
525
|
+
!r.x &&
|
526
|
+
!r.y)
|
527
|
+
return false;
|
528
|
+
return !(Math.floor(100 - ((r.top >= 0 ? 0 : r.top) / +-r.height) * 100) <
|
529
|
+
percentVisible ||
|
530
|
+
Math.floor(100 - ((r.bottom - windowHeight) / r.height) * 100) <
|
531
|
+
percentVisible);
|
532
|
+
}
|
533
|
+
/**
|
534
|
+
* Immutable array re-order
|
535
|
+
* @param from - the index to move from
|
536
|
+
* @param to - the index to move to
|
537
|
+
* @param arr - the array to re-order
|
538
|
+
* @returns - a new, re-orderd array
|
539
|
+
*/
|
540
|
+
function arrMove(from, to, arr) {
|
541
|
+
const newArr = [...arr];
|
542
|
+
const item = newArr.splice(from, 1)[0];
|
543
|
+
newArr.splice(to, 0, item);
|
544
|
+
return newArr;
|
545
|
+
}
|
546
|
+
|
547
|
+
// TABLE HEADERS
|
548
|
+
// (thead > tr > th, tfoot > tr > th)
|
549
|
+
let draggingCol;
|
550
|
+
let draggingColEle;
|
551
|
+
let dragEnterEle;
|
552
|
+
let draggingParent;
|
553
|
+
const TableColHead = ({ column, headRenderer, onColumnSortClick, onColumnPinned, onColumnDrag, onColumnDrop, defaults, }) => {
|
554
|
+
const store = fetchStores();
|
555
|
+
// Drag to re-order columns handling
|
556
|
+
function handleDragStart(e, column) {
|
557
|
+
draggingCol = column;
|
558
|
+
draggingColEle = dragEnterEle = e.target;
|
559
|
+
draggingParent = draggingColEle.closest('.' + `${CSSNAMESPACE}__tr`);
|
560
|
+
draggingParent.classList.add(`${CSSNAMESPACE}__dragging`);
|
561
|
+
draggingColEle.classList.add(`${CSSNAMESPACE}__drag--start`);
|
562
|
+
e.dataTransfer.effectAllowed = 'move';
|
563
|
+
e.dataTransfer.setData('text/html', draggingColEle.innerHTML);
|
564
|
+
onColumnDrag(column.prop, draggingColEle);
|
565
|
+
}
|
566
|
+
function handleDragEnd() {
|
567
|
+
draggingParent.classList.remove(`${CSSNAMESPACE}__dragging`);
|
568
|
+
draggingColEle.classList.remove(`${CSSNAMESPACE}__drag--start`);
|
569
|
+
draggingParent
|
570
|
+
.querySelectorAll(`.${CSSNAMESPACE}__drag-mask--active`)
|
571
|
+
.forEach((el) => {
|
572
|
+
el.classList.remove(`${CSSNAMESPACE}__drag-mask--active`);
|
573
|
+
});
|
574
|
+
draggingColEle = null;
|
575
|
+
draggingCol = null;
|
576
|
+
draggingParent = null;
|
577
|
+
dragEnterEle = null;
|
578
|
+
}
|
579
|
+
function handleDragEnter(e) {
|
580
|
+
if (dragEnterEle === e.target)
|
581
|
+
return;
|
582
|
+
e.preventDefault();
|
583
|
+
e.stopImmediatePropagation();
|
584
|
+
e.dataTransfer.dropEffect = 'move';
|
585
|
+
draggingParent
|
586
|
+
.querySelectorAll(`.${CSSNAMESPACE}__drag-mask--active`)
|
587
|
+
.forEach((el) => {
|
588
|
+
el.classList.remove(`${CSSNAMESPACE}__drag-mask--active`);
|
589
|
+
});
|
590
|
+
dragEnterEle = e.target;
|
591
|
+
if (!dragEnterEle.classList.contains(`${CSSNAMESPACE}__drag-mask`)) {
|
592
|
+
e.dataTransfer.dropEffect = 'none';
|
593
|
+
return;
|
594
|
+
}
|
595
|
+
dragEnterEle.classList.add(`${CSSNAMESPACE}__drag-mask--active`);
|
596
|
+
}
|
597
|
+
function handleDrop(e) {
|
598
|
+
e.stopPropagation();
|
599
|
+
const { colName } = this.dataset;
|
600
|
+
if (colName === draggingCol.prop)
|
601
|
+
return;
|
602
|
+
const cols = store.config.state.columns;
|
603
|
+
let toIndex = cols.findIndex((col) => col.prop === colName);
|
604
|
+
const fromIndex = cols.findIndex((col) => col === draggingCol);
|
605
|
+
if (toIndex < fromIndex &&
|
606
|
+
this.classList.contains(`${CSSNAMESPACE}__drag-mask--end`))
|
607
|
+
toIndex++;
|
608
|
+
if (toIndex > fromIndex &&
|
609
|
+
this.classList.contains(`${CSSNAMESPACE}__drag-mask--start`))
|
610
|
+
toIndex--;
|
611
|
+
if (toIndex === fromIndex)
|
612
|
+
return;
|
613
|
+
onColumnDrop(draggingCol.prop, store.config.state.columns[toIndex].prop, draggingColEle);
|
614
|
+
}
|
615
|
+
// Sort handling
|
616
|
+
function handleColumnSortClick(e) {
|
617
|
+
let order;
|
618
|
+
switch (column.order) {
|
619
|
+
case 'asc':
|
620
|
+
order = 'desc';
|
621
|
+
break;
|
622
|
+
case 'desc':
|
623
|
+
order = null;
|
624
|
+
break;
|
625
|
+
default:
|
626
|
+
order = 'asc';
|
627
|
+
}
|
628
|
+
onColumnSortClick(order, column.prop, e.target.closest('th'));
|
629
|
+
}
|
630
|
+
function isSortable() {
|
631
|
+
return ((!!defaults.sortable && column.sortable !== false) ||
|
632
|
+
(!defaults.sortable && column.sortable === true));
|
633
|
+
}
|
634
|
+
function isDraggable() {
|
635
|
+
return ((!!defaults.draggable && column.draggable !== false) ||
|
636
|
+
(!defaults.draggable && column.draggable === true));
|
637
|
+
}
|
638
|
+
let extraProps = {};
|
639
|
+
if (column.columnProperties) {
|
640
|
+
extraProps = column.columnProperties(column) || extraProps;
|
641
|
+
}
|
642
|
+
const baseProps = {
|
643
|
+
class: {
|
644
|
+
...headerPinClasses('th', headRenderer?.pinned),
|
645
|
+
[`${CSSNAMESPACE}__pin--start`]: column.pinned === 'start',
|
646
|
+
[`${CSSNAMESPACE}__pin--end`]: column.pinned === 'end',
|
647
|
+
[`${CSSNAMESPACE}__ordered`]: !!column.order,
|
648
|
+
[`${CSSNAMESPACE}__filtered`]: !!column.filter,
|
649
|
+
},
|
650
|
+
};
|
651
|
+
let props = extraProps ? mergeProperties(baseProps, extraProps) : baseProps;
|
652
|
+
const content = colheadFootRender(column);
|
653
|
+
if (!content)
|
654
|
+
return h(Fragment, null);
|
655
|
+
props =
|
656
|
+
Number(props.colSpan) > 1
|
657
|
+
? { ...props, scope: 'colgroup' }
|
658
|
+
: { ...props, scope: 'col' };
|
659
|
+
if (isSortable()) {
|
660
|
+
const sort = column.order
|
661
|
+
? column.order === 'asc'
|
662
|
+
? 'ascending'
|
663
|
+
: 'descending'
|
664
|
+
: 'none';
|
665
|
+
props = { ...props, 'aria-sort': sort };
|
666
|
+
}
|
667
|
+
if (isDraggable()) {
|
668
|
+
props = {
|
669
|
+
...props,
|
670
|
+
draggable: true,
|
671
|
+
onDragStart: (e) => handleDragStart(e, column),
|
672
|
+
onDragOver: (e) => e.preventDefault(),
|
673
|
+
onDragEnd: () => handleDragEnd(),
|
674
|
+
};
|
675
|
+
}
|
676
|
+
return (h("th", { ...props, ref: (th) => {
|
677
|
+
if (['end', 'start'].includes(column.pinned))
|
678
|
+
addHObserver(th, column.pinned, onColumnPinned);
|
679
|
+
if (['top', 'bottom'].includes(headRenderer.pinned))
|
680
|
+
addVObserver(th, headRenderer.pinned, onColumnPinned);
|
681
|
+
}, key: column.prop },
|
682
|
+
isDraggable() && [
|
683
|
+
h("div", { class: {
|
684
|
+
[`${CSSNAMESPACE}__drag-mask`]: true,
|
685
|
+
[`${CSSNAMESPACE}__drag-mask--start`]: true,
|
686
|
+
}, "data-col-name": column.prop, onDragEnter: handleDragEnter, onDrop: handleDrop, onDragOver: (e) => e.preventDefault() }),
|
687
|
+
h("div", { class: {
|
688
|
+
[`${CSSNAMESPACE}__drag-mask`]: true,
|
689
|
+
[`${CSSNAMESPACE}__drag-mask--end`]: true,
|
690
|
+
}, "data-col-name": column.prop, onDragEnter: handleDragEnter, onDrop: handleDrop, onDragOver: (e) => e.preventDefault() }),
|
691
|
+
],
|
692
|
+
isSortable() ? (h("button", { class: {
|
693
|
+
[`${CSSNAMESPACE}__order-btn`]: true,
|
694
|
+
[`${CSSNAMESPACE}__cell-content`]: true,
|
695
|
+
}, onClick: handleColumnSortClick },
|
696
|
+
colheadFootRender(column),
|
697
|
+
!!column.filter && h("nano-icon", { name: "light/filter" }),
|
698
|
+
!!column.order &&
|
699
|
+
(column.order === 'desc' ? (h("nano-icon", { name: "solid/long-arrow-down" })) : (h("nano-icon", { name: "solid/long-arrow-up" }))),
|
700
|
+
h("div", { class: `${CSSNAMESPACE}__status-icons` },
|
701
|
+
h("nano-icon", { name: "light/chevron-down" })))) : (h("div", { class: `${CSSNAMESPACE}__cell-content` },
|
702
|
+
colheadFootRender(column),
|
703
|
+
!!column.filter && h("nano-icon", { name: "light/bars-filter" })))));
|
704
|
+
};
|
705
|
+
|
706
|
+
// TABLE CELL
|
707
|
+
// (tbody > tr > td, tbody > tr > th)
|
708
|
+
/**
|
709
|
+
* Renders a cell using a custom renderer if set.
|
710
|
+
* @param rowIndex - the current row index being rendered
|
711
|
+
* @param colIndex - the current column index being rendered
|
712
|
+
* @returns - a JSX node
|
713
|
+
*/
|
714
|
+
function cellRender(rowIndex, colIndex) {
|
715
|
+
const store = fetchStores();
|
716
|
+
const columns = store.config.state.columns;
|
717
|
+
const tpl = columns[colIndex]?.cellTemplate;
|
718
|
+
const model = colDataModel(rowIndex, colIndex);
|
719
|
+
if (!!model.cellModel && columns[colIndex].type === 'date') {
|
720
|
+
const d = new Date(model.cellModel);
|
721
|
+
if (d instanceof Date && !isNaN(d)) {
|
722
|
+
model.cellModel = !tpl
|
723
|
+
? `${new Date(model.cellModel).toLocaleDateString()} ${new Date(model.cellModel).toLocaleTimeString()}`
|
724
|
+
: d;
|
725
|
+
}
|
726
|
+
}
|
727
|
+
return tpl ? (tpl(h, model)) : (h(Fragment, null, model.cellModel?.toString()));
|
728
|
+
}
|
729
|
+
const baseCellClasses = (colIndex, toString = false) => {
|
730
|
+
const store = fetchStores();
|
731
|
+
const column = store.config.state.columns[colIndex];
|
732
|
+
const classes = {
|
733
|
+
[`${CSSNAMESPACE}__td`]: true,
|
734
|
+
[`${CSSNAMESPACE}__ordered`]: !!column.order,
|
735
|
+
[`${CSSNAMESPACE}__pin`]: !!column.pinned,
|
736
|
+
[`${CSSNAMESPACE}__pin--start`]: column.pinned === 'start',
|
737
|
+
[`${CSSNAMESPACE}__pin--end`]: column.pinned === 'end',
|
738
|
+
};
|
739
|
+
if (toString)
|
740
|
+
return classListToStr(classes);
|
741
|
+
return classes;
|
742
|
+
};
|
743
|
+
const TableCell = ({ rowIndex, colIndex, nestedContent, }) => {
|
744
|
+
const content = nestedContent || cellRender(rowIndex, colIndex);
|
745
|
+
if (!content)
|
746
|
+
return h(Fragment, null);
|
747
|
+
let CellType = 'td';
|
748
|
+
const store = fetchStores();
|
749
|
+
const column = store.config.state.columns[colIndex];
|
750
|
+
let props = mergeCellProperties(rowIndex, colIndex, {
|
751
|
+
class: baseCellClasses(colIndex),
|
752
|
+
});
|
753
|
+
if (column.rowHeader) {
|
754
|
+
props =
|
755
|
+
Number(props.rowSpan) > 1
|
756
|
+
? { ...props, scope: 'rowgroup' }
|
757
|
+
: { ...props, scope: 'row' };
|
758
|
+
CellType = 'th';
|
759
|
+
}
|
760
|
+
return (h(CellType
|
761
|
+
// role="gridcell"
|
762
|
+
, { ...props },
|
763
|
+
h("div", { class: `${CSSNAMESPACE}__cell-content` }, content)));
|
764
|
+
};
|
765
|
+
|
766
|
+
const TableRow = ({ rowRenderer, rowIndex, row, onColumnPinned }, children, utils) => {
|
767
|
+
let extraProps = {};
|
768
|
+
const TableCell = ({ header }, children) => {
|
769
|
+
const cell = h("div", { class: CSSNAMESPACE + '__cell-content' }, children);
|
770
|
+
return header ? h("th", { scope: "row" }, cell) : h("td", null, cell);
|
771
|
+
};
|
772
|
+
if (!row) {
|
773
|
+
const model = rowDataModel(rowIndex);
|
774
|
+
row = model.row;
|
775
|
+
}
|
776
|
+
if (rowRenderer?.rowProperties) {
|
777
|
+
extraProps = rowRenderer.rowProperties({ row, rowIndex }) || extraProps;
|
778
|
+
}
|
779
|
+
let pinned;
|
780
|
+
if (rowRenderer?.pinned && typeof rowRenderer.pinned === 'function') {
|
781
|
+
pinned = rowRenderer.pinned();
|
782
|
+
}
|
783
|
+
const baseProps = { class: headerPinClasses('tr', pinned) };
|
784
|
+
const props = extraProps ? mergeProperties(baseProps, extraProps) : baseProps;
|
785
|
+
const tpl = rowRenderer?.template;
|
786
|
+
if (tpl) {
|
787
|
+
let toRender = tpl(h, {
|
788
|
+
renderedRow: (h("tr", { ...props, key: row.__uuid }, children)),
|
789
|
+
row,
|
790
|
+
rowIndex,
|
791
|
+
}, TableCell);
|
792
|
+
if (Array.isArray(toRender)) {
|
793
|
+
toRender = utils.map(toRender, (node, i) => {
|
794
|
+
if (node.vtag === 'tr') {
|
795
|
+
if (!node.vkey)
|
796
|
+
node.vkey = `${row.__uuid}_${i}`;
|
797
|
+
node.vattrs = mergeProperties({ class: headerPinClasses('tr', pinned, true) }, node.vattrs);
|
798
|
+
if (!!node.vchildren) {
|
799
|
+
node.vchildren = utils.map(node.vchildren, (cNode, i) => {
|
800
|
+
if (['td', 'th'].includes(cNode.vtag.toString())) {
|
801
|
+
cNode.vattrs = mergeProperties({
|
802
|
+
class: headerPinClasses(cNode.vtag.toString(), pinned, true) + baseCellClasses(i, true),
|
803
|
+
ref: (th) => {
|
804
|
+
if ((!!th && pinned === 'top') || pinned === 'bottom')
|
805
|
+
addVObserver(th, pinned, onColumnPinned);
|
806
|
+
if (!!th && th.classList.contains('nano-tbl__pin--end'))
|
807
|
+
addHObserver(th, 'end', onColumnPinned);
|
808
|
+
if (!!th && th.classList.contains('nano-tbl__pin--start'))
|
809
|
+
addHObserver(th, 'start', onColumnPinned);
|
810
|
+
},
|
811
|
+
}, cNode.vattrs);
|
812
|
+
}
|
813
|
+
return cNode;
|
814
|
+
});
|
815
|
+
}
|
816
|
+
}
|
817
|
+
return node;
|
818
|
+
});
|
819
|
+
}
|
820
|
+
return toRender;
|
821
|
+
}
|
822
|
+
return (h("tr", { ...props, key: row.__uuid }, children));
|
823
|
+
};
|
824
|
+
const TableHeadFootRow = ({ rowRenderer, onColumnPinned }, children, utils) => {
|
825
|
+
let extraProps = {};
|
826
|
+
if (rowRenderer.rowProperties) {
|
827
|
+
extraProps = rowRenderer.rowProperties() || {};
|
828
|
+
}
|
829
|
+
const TableCell = ({ header }, children) => {
|
830
|
+
const cell = h("div", { class: CSSNAMESPACE + '__cell-content' }, children);
|
831
|
+
return header !== false ? h("th", { scope: "col" }, cell) : h("td", null, cell);
|
832
|
+
};
|
833
|
+
const pinned = rowRenderer.pinned || null;
|
834
|
+
const baseProps = { class: headerPinClasses('tr', null) };
|
835
|
+
const props = extraProps ? mergeProperties(baseProps, extraProps) : baseProps;
|
836
|
+
const tpl = rowRenderer?.template;
|
837
|
+
if (tpl) {
|
838
|
+
let toRender = tpl(h, {
|
839
|
+
renderedRow: h("tr", { ...props }, children),
|
840
|
+
}, TableCell);
|
841
|
+
if (Array.isArray(toRender)) {
|
842
|
+
toRender = utils.map(toRender, (node) => {
|
843
|
+
if (node.vtag === 'tr') {
|
844
|
+
node.vattrs = mergeProperties({ class: headerPinClasses('tr', pinned, true) }, node.vattrs);
|
845
|
+
if (!!node.vchildren) {
|
846
|
+
node.vchildren = utils.map(node.vchildren, (cNode) => {
|
847
|
+
if (['td', 'th'].includes(cNode.vtag.toString())) {
|
848
|
+
cNode.vattrs = mergeProperties({
|
849
|
+
class: headerPinClasses(cNode.vtag.toString(), pinned, true),
|
850
|
+
ref: (th) => {
|
851
|
+
if ((!!th && pinned === 'top') || pinned === 'bottom')
|
852
|
+
addVObserver(th, pinned, onColumnPinned);
|
853
|
+
if (!!th && th.classList.contains('nano-tbl__pin--end'))
|
854
|
+
addHObserver(th, 'end', onColumnPinned);
|
855
|
+
if (!!th && th.classList.contains('nano-tbl__pin--start'))
|
856
|
+
addHObserver(th, 'start', onColumnPinned);
|
857
|
+
},
|
858
|
+
}, cNode.vattrs);
|
859
|
+
}
|
860
|
+
return cNode;
|
861
|
+
});
|
862
|
+
}
|
863
|
+
}
|
864
|
+
return node;
|
865
|
+
});
|
866
|
+
}
|
867
|
+
return toRender;
|
868
|
+
}
|
869
|
+
return h("tr", { ...props }, children);
|
870
|
+
};
|
871
|
+
|
872
|
+
const tableCss = ":host{box-sizing:border-box}*,*::before,*::after{box-sizing:border-box}[hidden]{display:none !important}nano-table{display:block;width:100%;--max-col-width:clamp(200px, 30%, 20vw);--color:var(--nano-color-mediumgrey, #68767e);--font-size:0.87rem;--cell-line-height:1.5;--thead-font-size:0.95rem;--thead-color:#455560;--tfoot-color:#455560;--border-color:#dddbda;--border-style:thin solid var(--border-color);--border-tint-color:#0084a9;--border-tint-style:3px solid var(--border-tint-color);--cell-bg-rgb:var(--nano-color-white-rgb, 255 255 255);--head-bg-rgb:250, 250, 249;--foot-bg-rgb:var(--head-bg-rgb);--th-row-bg-rgb:var(--cell-bg-rgb);--ordered-bg-rgb:var(--nano-color-offwhite-rgb, 249 249 251);--td-padding-start:0.625rem;--td-padding-end:0.625rem;--td-padding-top:0.5rem;--td-padding-bottom:0.4125rem;--th-padding-start:0.625rem;--th-padding-end:0.625rem;--th-padding-top:0.875rem;--th-padding-bottom:0.6875rem;--td-padding-v:var(--td-padding-top) var(--td-padding-bottom);--td-padding-h:var(--td-padding-start) var(--td-padding-end);--th-padding-v:var(--th-padding-top) var(--th-padding-bottom);--th-padding-h:var(--th-padding-start) var(--th-padding-end);--foot-th-padding-v:var(--td-padding-top) var(--td-padding-bottom);--foot-th-padding-h:var(--td-padding-start) var(--td-padding-end);--head-th-padding-v:var(--th-padding-top) var(--th-padding-bottom);--head-th-padding-h:var(--th-padding-start) var(--th-padding-end);--bookend-col-padding:2rem}.nano-tbl{color:var(--color);text-align:start;width:100%;font-size:var(--font-size);border-spacing:0 0;border-collapse:separate;background:rgb(var(--cell-bg-rgb));border-inline-end:1px solid transparent;border-block-start:1px solid transparent;position:relative;z-index:1}.nano-tbl__wrap{display:table;min-width:100%}.nano-tbl__top-anchor{clip:rect(1px, 1px, 1px, 1px);-webkit-clip-path:inset(50%);clip-path:inset(50%);block-size:1px;inline-size:1px;margin:-1px;overflow:hidden;padding:0;position:absolute;position:relative}.nano-tbl__ordered{background-color:var(--ordered-bg);border-inline-start:var(--border-style);border-inline-end:var(--border-style)}.nano-tbl__drag-mask{display:none;position:absolute;inset-block:-2px -2px;z-index:10;opacity:0;transition:0.2s ease opacity}.nano-tbl__drag-mask--start{width:calc(50% + 2px);inset-inline-start:-2px;border-inline-start:2px dashed var(--border-tint-color)}.nano-tbl__drag-mask--end{width:50%;inset-inline-end:0;border-inline-end:2px dashed var(--border-tint-color)}.nano-tbl__drag-mask--active{opacity:1}.nano-tbl__dragging .nano-tbl__drag-mask{display:block}.nano-tbl__drag--start{opacity:0.4}.nano-tbl__dragging .nano-tbl__td,.nano-tbl__dragging .nano-tbl__th{cursor:no-drop}.nano-tbl__dragging .nano-tbl__td .nano-tbl__cell-content,.nano-tbl__dragging .nano-tbl__th .nano-tbl__cell-content{pointer-events:none}.nano-tbl__order-btn{padding:0;border:none;outline:none;font:inherit;background:none;-webkit-appearance:none;appearance:none;color:inherit;display:flex;gap:10px;align-items:center;width:100%}.nano-tbl__status-icons{margin-inline:auto 10px;display:flex;gap:10px}.nano-tbl__progress-bar{font-size:0.2rem;position:sticky;inset-block-start:0;inset-inline:0;z-index:10;transition:scale 0.25s;scale:0;width:100%;height:0}.nano-tbl__progress-bar--show{scale:1;height:auto}.nano-tbl__caption--hide{clip:rect(1px, 1px, 1px, 1px);-webkit-clip-path:inset(50%);clip-path:inset(50%);block-size:1px;inline-size:1px;margin:-1px;overflow:hidden;padding:0;position:absolute}.nano-tbl__td,.nano-tbl__th{line-height:var(--cell-line-height);text-align:start;border-block-start:var(--border-style);max-width:var(--max-col-width);background-color:rgb(var(--cell-bg-rgb))}tbody:first-of-type tr:first-child .nano-tbl__td,tbody:first-of-type tr:first-child .nano-tbl__th{border-block-start:none}tbody:last-of-type tr:last-child .nano-tbl__td,tbody:last-of-type tr:last-child .nano-tbl__th{border-block-end:var(--border-style)}.md .nano-tbl__td:first-child .nano-tbl__cell-content,.md .nano-tbl__th:first-child .nano-tbl__cell-content{padding-inline-start:var(--bookend-col-padding)}.md .nano-tbl__td:last-child .nano-tbl__cell-content,.md .nano-tbl__th:last-child .nano-tbl__cell-content{padding-inline-end:var(--bookend-col-padding)}@media (max-width: 768px){.nano-tbl__td:first-child .nano-tbl__cell-content,.nano-tbl__th:first-child .nano-tbl__cell-content{padding-inline-start:var(--td-padding-start) !important}.nano-tbl__td:last-child .nano-tbl__cell-content,.nano-tbl__th:last-child .nano-tbl__cell-content{padding-inline-end:var(--td-padding-end) !important}}thead .nano-tbl__td,thead .nano-tbl__th{color:var(--thead-color);font-weight:800;background:rgba(var(--head-bg-rgb), 90%);font-size:var(--thead-font-size);border-block-start:none !important;transition:all 0.2s ease}thead .nano-tbl__td .nano-tbl__cell-content,thead .nano-tbl__th .nano-tbl__cell-content{padding-block:var(--head-th-padding-v);padding-inline:var(--head-th-padding-h)}tfoot .nano-tbl__td,tfoot .nano-tbl__th{color:var(--tfoot-color);font-weight:800;border-block-start:none;background:rgba(var(--foot-bg-rgb), 90%);font-size:var(--thead-font-size)}tfoot .nano-tbl__td .nano-tbl__cell-content,tfoot .nano-tbl__th .nano-tbl__cell-content{padding-block:var(--foot-th-padding-v);padding-inline:var(--foot-th-padding-h)}.nano-tbl__td.nano-tbl__ordered,.nano-tbl__th.nano-tbl__ordered{background-color:rgba(var(--ordered-bg-rgb), 0.8) !important}.nano-tbl__cell-content{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;padding-block:var(--td-padding-v);padding-inline:var(--td-padding-h)}.nano-tbl__cell-content--no-result{padding-block:2rem}.nano-tbl tbody{will-change:scroll-position;visibility:visible}.nano-tbl tbody.nano-tbl__inactive{visibility:hidden}.nano-tbl th[scope=row]{font-weight:800;margin:0}.nano-tbl__pin{position:sticky;z-index:1}.nano-tbl__pin--start{inset-inline:-1px auto;transition:max-width 0.25s ease;z-index:2}.nano-tbl__pin--start::after{content:\"\";position:absolute;inset:0;box-shadow:5px 1px 4px 0 rgba(0, 0, 0, 0.2);opacity:0;z-index:-1}.nano-tbl__pinned--start .nano-tbl__pin--start{z-index:2;max-width:125px !important}.sm .nano-tbl__pinned--start .nano-tbl__pin--start{max-width:var(--max-col-width) !important}.nano-tbl__pinned--start .nano-tbl__pin--start::after{opacity:1}.nano-tbl__pin--end{z-index:2;}.nano-tbl__pin--start+.nano-tbl__pin--end{inset-inline:auto auto}.nano-tbl__pin--start+.nano-tbl__pin--end::after{display:none}.sm .nano-tbl__pin--end{inset-inline:auto -1px !important;max-width:min(50vw, 200px)}.sm .nano-tbl__pin--end::after{display:block !important;content:\"\";position:absolute;inset:0;box-shadow:-5px 1px 4px 0 rgba(0, 0, 0, 0.2);opacity:0;z-index:-1}.sm .nano-tbl__pinned--end .nano-tbl__pin--end{z-index:3}.sm .nano-tbl__pinned--end .nano-tbl__pin--end::after{opacity:1}.nano-tbl__pin--top{inset-block:-1px auto;z-index:3}.nano-tbl__pinned--top .nano-tbl__pin--top{z-index:4 !important}.nano-tbl__pin--bottom{inset-block:auto -1px;z-index:3}.nano-tbl__pinned--bottom .nano-tbl__pin--bottom{z-index:5 !important}.nano-tbl__pin--top.nano-tbl__pin--start{z-index:4}.nano-tbl__pinned--start .nano-tbl__pin--top.nano-tbl__pin--start{z-index:5 !important}.nano-tbl__pinned--top.nano-tbl__pinned--start .nano-tbl__pin--top.nano-tbl__pin--start{z-index:6 !important}.nano-tbl__pin--top.nano-tbl__pin--end{z-index:4}.nano-tbl__pinned--end .nano-tbl__pin--top.nano-tbl__pin--end{z-index:5 !important}.nano-tbl__pinned--top.nano-tbl__pinned--end .nano-tbl__pin--top.nano-tbl__pin--end{z-index:6 !important}.nano-tbl__pin--bottom.nano-tbl__pin--start{z-index:4}.nano-tbl__pinned--start .nano-tbl__pin--bottom.nano-tbl__pin--start{z-index:5 !important}.nano-tbl__pinned--bottom.nano-tbl__pinned--start .nano-tbl__pin--bottom.nano-tbl__pin--start{z-index:6 !important}.nano-tbl__pin--bottom.nano-tbl__pin--end{z-index:4}.nano-tbl__pinned--end .nano-tbl__pin--bottom.nano-tbl__pin--end{z-index:5 !important}.nano-tbl__pinned--bottom.nano-tbl__pinned--end .nano-tbl__pin--bottom.nano-tbl__pin--end{z-index:6 !important}.nano-tbl thead tr:last-of-type td,.nano-tbl thead tr:last-of-type th{border-block-end:var(--border-tint-style)}.nano-tbl tfoot tr:first-of-type td,.nano-tbl tfoot tr:first-of-type th{border-block-start:none}.nano-tbl tfoot tr:last-of-type td,.nano-tbl tfoot tr:last-of-type th{border-block-end:var(--border-tint-style)}.nano-tbl__pinned--bottom .nano-tbl tfoot tr.nano-tbl__pin--bottom:first-of-type td,.nano-tbl__pinned--bottom .nano-tbl tfoot tr.nano-tbl__pin--bottom:first-of-type th{border-block-start:var(--border-tint-style) !important}.nano-tbl__pinned--bottom .nano-tbl tfoot tr.nano-tbl__pin--bottom:last-of-type td,.nano-tbl__pinned--bottom .nano-tbl tfoot tr.nano-tbl__pin--bottom:last-of-type th{border-block-end:none !important}.nano-tbl .unlimited-width{max-width:none}.nano-tbl__spinner{font-size:1.5rem;transition:scale 0.25s;scale:0;padding:0.5rem;position:absolute;inset-block-end:0;inset-inline-start:calc(50% - 0.75rem);z-index:0}.nano-tbl__spinner--show{scale:1;position:sticky}.nano-tbl nano-skeleton{line-height:var(--cell-line-height)}";
|
873
|
+
|
874
|
+
function perMark(name, end = false) {
|
875
|
+
if (!Build.isDev)
|
876
|
+
return;
|
877
|
+
if (end) {
|
878
|
+
performance?.mark('end' + name);
|
879
|
+
performance?.measure(name, 'start' + name, 'end' + name);
|
880
|
+
const entries = performance?.getEntriesByName(name);
|
881
|
+
console.log(entries[entries.length ? entries.length - 1 : 0]);
|
882
|
+
}
|
883
|
+
else {
|
884
|
+
performance?.mark('start' + name);
|
885
|
+
}
|
886
|
+
}
|
887
|
+
let id = 0;
|
888
|
+
const Table = /*@__PURE__*/ proxyCustomElement(class extends HTMLElement {
|
889
|
+
constructor() {
|
890
|
+
super();
|
891
|
+
this.__registerHost();
|
892
|
+
this.nanoTblBlockRendered = createEvent(this, "nanoTblBlockRendered", 7);
|
893
|
+
this.nanoTblReady = createEvent(this, "nanoTblReady", 7);
|
894
|
+
this.nanoTblBeforeSort = createEvent(this, "nanoTblBeforeSort", 7);
|
895
|
+
this.nanoTblAfterSort = createEvent(this, "nanoTblAfterSort", 7);
|
896
|
+
this.nanoTblColDrag = createEvent(this, "nanoTblColDrag", 7);
|
897
|
+
this.nanoTblColDrop = createEvent(this, "nanoTblColDrop", 7);
|
898
|
+
this.nanoTblBeforeFilter = createEvent(this, "nanoTblBeforeFilter", 7);
|
899
|
+
this.nanoTblAfterFilter = createEvent(this, "nanoTblAfterFilter", 7);
|
900
|
+
this.nanoTblBeforeSearch = createEvent(this, "nanoTblBeforeSearch", 7);
|
901
|
+
this.nanoTblAfterSearch = createEvent(this, "nanoTblAfterSearch", 7);
|
902
|
+
this.debounceSetLoading = (l) => {
|
903
|
+
this._loading = l;
|
904
|
+
};
|
905
|
+
this.renderId = 'tbl-' + id++;
|
906
|
+
this.filters = [];
|
907
|
+
this.currentFilters = '[]';
|
908
|
+
this.currentSort = '';
|
909
|
+
this.blockIos = new WeakMap();
|
910
|
+
this.blockHeights = [];
|
911
|
+
this.unitHeight = 0;
|
912
|
+
// Scroll / IO used for hiding / showing blocks
|
913
|
+
this.ignoreIO = true;
|
914
|
+
this._isReady = false;
|
915
|
+
this.colDrag = (column) => {
|
916
|
+
this.nanoTblColDrag.emit({ column });
|
917
|
+
};
|
918
|
+
this.colDrop = (fromCol, toCol) => {
|
919
|
+
const cols = this.store.config.state.columns;
|
920
|
+
const toIndex = cols.findIndex((col) => col.prop === toCol);
|
921
|
+
const fromIndex = cols.findIndex((col) => col.prop === fromCol);
|
922
|
+
const dropEvent = this.nanoTblColDrop.emit({
|
923
|
+
fromCol,
|
924
|
+
toCol,
|
925
|
+
fromIndex,
|
926
|
+
toIndex,
|
927
|
+
});
|
928
|
+
if (dropEvent.defaultPrevented)
|
929
|
+
return;
|
930
|
+
this.store.config.state.columns = arrMove(fromIndex, toIndex, cols);
|
931
|
+
};
|
932
|
+
/**
|
933
|
+
* Start a sort - can be cancelled by `preventDefault`
|
934
|
+
* @param order - column order
|
935
|
+
* @param column - column config object
|
936
|
+
* @returns A promise
|
937
|
+
*/
|
938
|
+
this.sortStart = async (order, column, element) => {
|
939
|
+
// did order change?
|
940
|
+
if (this.currentSort === order + ':' + column)
|
941
|
+
return;
|
942
|
+
this.loading = true;
|
943
|
+
const sortEvent = this.nanoTblBeforeSort.emit({ column: column, order });
|
944
|
+
if (sortEvent.defaultPrevented)
|
945
|
+
return;
|
946
|
+
perMark('sort');
|
947
|
+
this.currentSort = order + ':' + column;
|
948
|
+
// doesn't make sense to leave user in place for a sort
|
949
|
+
this.scrollToTop(element);
|
950
|
+
if (this.customSortFn) {
|
951
|
+
try {
|
952
|
+
await this.customSortFn(column, order);
|
953
|
+
this.sortComplete(order, column);
|
954
|
+
}
|
955
|
+
catch (e) {
|
956
|
+
console.warn('custom sort failed', e);
|
957
|
+
}
|
958
|
+
return;
|
959
|
+
}
|
960
|
+
try {
|
961
|
+
await storeSort(this.host, column, order);
|
962
|
+
this.sortComplete(order, column);
|
963
|
+
}
|
964
|
+
catch (e) {
|
965
|
+
console.warn('sort failed', e);
|
966
|
+
this.currentSort = '';
|
967
|
+
}
|
968
|
+
finally {
|
969
|
+
this.loading = false;
|
970
|
+
}
|
971
|
+
};
|
972
|
+
/**
|
973
|
+
* Attaches an intersection observer to each rendered tbody element
|
974
|
+
* shows / hides intersecting blocks' and sets heights for when they're hidden
|
975
|
+
* @param el - the tbody element to observe
|
976
|
+
* @param blockIndex - the rendering tbody we're attaching the IO to
|
977
|
+
*/
|
978
|
+
this.setupBlockIO = (el, blockIndex) => {
|
979
|
+
if (!el || this.blockIos.has(el))
|
980
|
+
return;
|
981
|
+
const blockIo = new IntersectionObserver(([ioEntry]) => {
|
982
|
+
if (this.ignoreIO)
|
983
|
+
return;
|
984
|
+
if (ioEntry.isIntersecting) {
|
985
|
+
// This is a bit gross
|
986
|
+
// The Intersection Observer (IO) fires in an incorrect order when the scrolling is very fast
|
987
|
+
// i.e. we go past blocks 3, 2, 1 and land on 0, but 3 can fire as 'intersecting' after 0.
|
988
|
+
// To fix that, we check - for realzies - if the block IS visible.
|
989
|
+
// BUT that test is not as sensitive to a block being visible via the IO,
|
990
|
+
// so doesn't always fire if scrolling slowly
|
991
|
+
// *sigh*
|
992
|
+
readTask(() => {
|
993
|
+
if (this.scrollSpeed < 100 || isInViewport(el, 0.01)) {
|
994
|
+
this.activeBlocks = [
|
995
|
+
blockIndex,
|
996
|
+
blockIndex + 1,
|
997
|
+
Math.max(0, blockIndex - 1),
|
998
|
+
];
|
999
|
+
this.nanoTblBlockRendered.emit({
|
1000
|
+
block: blockIndex,
|
1001
|
+
totalBlocks: this.blockElements.length,
|
1002
|
+
});
|
1003
|
+
requestAnimationFrame(() => this.setBlockHeight());
|
1004
|
+
}
|
1005
|
+
});
|
1006
|
+
}
|
1007
|
+
}, {
|
1008
|
+
threshold: [0],
|
1009
|
+
root: this.scrollParent === document.scrollingElement
|
1010
|
+
? null
|
1011
|
+
: this.scrollParent,
|
1012
|
+
});
|
1013
|
+
blockIo.observe(el);
|
1014
|
+
this.blockIos.set(el, blockIo);
|
1015
|
+
};
|
1016
|
+
this.handleColumnPinned = (positions) => {
|
1017
|
+
Object.entries(positions).forEach(([key, applied]) => {
|
1018
|
+
this.tableEle.classList.toggle(`${CSSNAMESPACE}__pinned--${key}`, applied);
|
1019
|
+
});
|
1020
|
+
};
|
1021
|
+
this.handleResizeChange = (e) => {
|
1022
|
+
this.tableWrapperEle.className = '';
|
1023
|
+
this.tableWrapperEle.classList.add(...(e.target.className + ' ' + `${CSSNAMESPACE}__wrap`).split(' '));
|
1024
|
+
};
|
1025
|
+
this.type = 'table';
|
1026
|
+
this.caption = undefined;
|
1027
|
+
this.showCaption = false;
|
1028
|
+
this._loading = false;
|
1029
|
+
this.placeholderSize = 5;
|
1030
|
+
this.rows = undefined;
|
1031
|
+
this.columns = [];
|
1032
|
+
this.headRender = { pinned: 'top' };
|
1033
|
+
this.rowRender = undefined;
|
1034
|
+
this.footRender = { pinned: 'bottom' };
|
1035
|
+
this.showFooter = false;
|
1036
|
+
this.perBlock = 60;
|
1037
|
+
this.searchTerm = undefined;
|
1038
|
+
this.customFilterFn = undefined;
|
1039
|
+
this.customSortFn = undefined;
|
1040
|
+
this.defaultSort = true;
|
1041
|
+
this.defaultColDraggable = false;
|
1042
|
+
this.blocks = [];
|
1043
|
+
this.activeBlocks = [0, 1, 2];
|
1044
|
+
this.measureHeight = 0;
|
1045
|
+
this.debounceSetLoading = debounce(this.debounceSetLoading.bind(this), 50);
|
1046
|
+
}
|
1047
|
+
/** Will show a loading state when true.
|
1048
|
+
* Will be shown automatically if `rows` is a promise waiting to resolve
|
1049
|
+
* or when performing custom filtering or sorting */
|
1050
|
+
get loading() {
|
1051
|
+
return this._loading;
|
1052
|
+
}
|
1053
|
+
set loading(l) {
|
1054
|
+
this.debounceSetLoading(l);
|
1055
|
+
}
|
1056
|
+
handleRowsChange() {
|
1057
|
+
if (!this.rows) {
|
1058
|
+
this.loading = true;
|
1059
|
+
return;
|
1060
|
+
}
|
1061
|
+
this.loading = true;
|
1062
|
+
Promise.resolve(this.rows).then(async (rows) => {
|
1063
|
+
await storeSetData(this.host, rows);
|
1064
|
+
// reset everything
|
1065
|
+
this.currentFilters = '';
|
1066
|
+
this.currentSort = '';
|
1067
|
+
await this.columnInit();
|
1068
|
+
if (!this.isReady)
|
1069
|
+
this.setInitialBlockDimension();
|
1070
|
+
this.loading = false;
|
1071
|
+
});
|
1072
|
+
}
|
1073
|
+
async handleColsChange() {
|
1074
|
+
await storeSetConfig(this.host, this.columns);
|
1075
|
+
if (this.isReady)
|
1076
|
+
this.columnInit();
|
1077
|
+
}
|
1078
|
+
/** The number of total blocks currently rendered in the table. @readonly */
|
1079
|
+
get blocksLength() {
|
1080
|
+
return this.blocks.length;
|
1081
|
+
}
|
1082
|
+
handleSearchTermChange() {
|
1083
|
+
this.searchStart();
|
1084
|
+
}
|
1085
|
+
/** Remove any column sorts currently applied
|
1086
|
+
* @returns a promise which resolves when complete */
|
1087
|
+
async resetSorting() {
|
1088
|
+
const col = this.columns.find((c) => !!c.order);
|
1089
|
+
if (!col)
|
1090
|
+
return;
|
1091
|
+
return this.sortStart(null, col.prop);
|
1092
|
+
}
|
1093
|
+
/** Apply a sort on a column
|
1094
|
+
* @returns a promise which resolves when complete */
|
1095
|
+
async addSort(column, order) {
|
1096
|
+
const col = this.columns.find((c) => c.prop === column);
|
1097
|
+
if (!col)
|
1098
|
+
throw 'Cannot find column with ' + column;
|
1099
|
+
return this.sortStart(order, col.prop);
|
1100
|
+
}
|
1101
|
+
/** Remove any column filters currently applied
|
1102
|
+
* @returns a promise which resolves when complete */
|
1103
|
+
async resetFilters() {
|
1104
|
+
this.filters = [];
|
1105
|
+
return this.filterStart();
|
1106
|
+
}
|
1107
|
+
/** Apply a filter on a column
|
1108
|
+
* @param filters - the filters to apply
|
1109
|
+
* @param additive - if true, will add the filters to any currently applied
|
1110
|
+
* @returns a promise which resolves when complete
|
1111
|
+
*/
|
1112
|
+
async addFilters(filters, additive = true) {
|
1113
|
+
if (!additive)
|
1114
|
+
this.filters = [];
|
1115
|
+
return this.filterStart(filters, additive);
|
1116
|
+
}
|
1117
|
+
/** Remove filters from a column
|
1118
|
+
* @param columnNames - the filters to apply
|
1119
|
+
* @returns a promise which resolves when complete
|
1120
|
+
*/
|
1121
|
+
async removeFilters(columnNames) {
|
1122
|
+
this.filters = this.filters.filter((f) => !columnNames.includes(f.prop));
|
1123
|
+
return this.filterStart();
|
1124
|
+
}
|
1125
|
+
// uses the first 'tr' of an active block as our yard stick
|
1126
|
+
set measureEle(el) {
|
1127
|
+
if (!el)
|
1128
|
+
return;
|
1129
|
+
this.measureHeight = el.getBoundingClientRect().height;
|
1130
|
+
this.unitHeight =
|
1131
|
+
el.querySelector('tr')?.getBoundingClientRect().height || this.unitHeight;
|
1132
|
+
}
|
1133
|
+
// Misc.
|
1134
|
+
get isReady() {
|
1135
|
+
return this._isReady;
|
1136
|
+
}
|
1137
|
+
set isReady(ready) {
|
1138
|
+
if (ready === this._isReady)
|
1139
|
+
return;
|
1140
|
+
this._isReady = ready;
|
1141
|
+
if (this.isReady)
|
1142
|
+
requestAnimationFrame(() => this.handleReady());
|
1143
|
+
}
|
1144
|
+
// Private Logic
|
1145
|
+
// Listeners
|
1146
|
+
scrollListener() {
|
1147
|
+
// see explanation in `setupBlockIO`
|
1148
|
+
const speed = detectScrollSpeed();
|
1149
|
+
this.scrollSpeed = speed < 0 ? speed * -1 : speed;
|
1150
|
+
}
|
1151
|
+
handleReady() {
|
1152
|
+
const hash = window.location.hash;
|
1153
|
+
if (hash.length > 1) {
|
1154
|
+
const idRow = document.querySelector(hash);
|
1155
|
+
if (idRow) {
|
1156
|
+
idRow.scrollIntoView();
|
1157
|
+
}
|
1158
|
+
}
|
1159
|
+
this.nanoTblReady.emit();
|
1160
|
+
}
|
1161
|
+
/**
|
1162
|
+
* Complete a sort. Reflects the order to ui.
|
1163
|
+
* @param order - column order
|
1164
|
+
* @param column - column config object
|
1165
|
+
*/
|
1166
|
+
sortComplete(order, column) {
|
1167
|
+
this.columns = this.columns.map((c) => {
|
1168
|
+
if (c.prop === column)
|
1169
|
+
return { ...c, order };
|
1170
|
+
return { ...c, order: null };
|
1171
|
+
});
|
1172
|
+
this.nanoTblAfterSort.emit({ column: column, order });
|
1173
|
+
perMark('sort', true);
|
1174
|
+
}
|
1175
|
+
async searchStart() {
|
1176
|
+
this.loading = true;
|
1177
|
+
const sortEvent = this.nanoTblBeforeSearch.emit({ term: this.searchTerm });
|
1178
|
+
if (sortEvent.defaultPrevented)
|
1179
|
+
return;
|
1180
|
+
perMark('search');
|
1181
|
+
// doesn't make sense to leave user in place for a search
|
1182
|
+
this.scrollToTop();
|
1183
|
+
try {
|
1184
|
+
await storeSearch(this.host, this.searchTerm);
|
1185
|
+
this.nanoTblAfterSearch.emit({ term: this.searchTerm });
|
1186
|
+
perMark('search', true);
|
1187
|
+
}
|
1188
|
+
catch (e) {
|
1189
|
+
console.warn('search failed', e);
|
1190
|
+
}
|
1191
|
+
finally {
|
1192
|
+
this.loading = false;
|
1193
|
+
}
|
1194
|
+
}
|
1195
|
+
async filterStart(filters, additive = true) {
|
1196
|
+
if (filters) {
|
1197
|
+
if (additive) {
|
1198
|
+
this.filters = [
|
1199
|
+
...this.filters.filter((f) => !filters.find((ff) => ff.prop === f.prop)),
|
1200
|
+
...filters,
|
1201
|
+
];
|
1202
|
+
}
|
1203
|
+
else {
|
1204
|
+
this.filters = filters;
|
1205
|
+
}
|
1206
|
+
}
|
1207
|
+
if (this.currentFilters === JSON.stringify(this.filters))
|
1208
|
+
return;
|
1209
|
+
this.loading = true;
|
1210
|
+
const sortEvent = this.nanoTblBeforeFilter.emit({ filters: this.filters });
|
1211
|
+
if (sortEvent.defaultPrevented)
|
1212
|
+
return;
|
1213
|
+
perMark('filter');
|
1214
|
+
this.currentFilters = JSON.stringify(this.filters);
|
1215
|
+
// doesn't make sense to leave user in place for a search
|
1216
|
+
this.scrollToTop();
|
1217
|
+
if (this.customFilterFn) {
|
1218
|
+
try {
|
1219
|
+
await this.customFilterFn(this.filters);
|
1220
|
+
this.filterComplete();
|
1221
|
+
}
|
1222
|
+
catch (e) {
|
1223
|
+
console.warn('custom filter failed', e);
|
1224
|
+
this.currentFilters = '';
|
1225
|
+
}
|
1226
|
+
return;
|
1227
|
+
}
|
1228
|
+
try {
|
1229
|
+
await storeFilter(this.host, this.filters);
|
1230
|
+
this.filterComplete();
|
1231
|
+
}
|
1232
|
+
catch (e) {
|
1233
|
+
console.warn('filter failed', e);
|
1234
|
+
}
|
1235
|
+
finally {
|
1236
|
+
this.loading = false;
|
1237
|
+
}
|
1238
|
+
}
|
1239
|
+
filterComplete() {
|
1240
|
+
this.columns = this.columns.map((c) => {
|
1241
|
+
const cFilter = this.filters.find((f) => f.prop === c.prop);
|
1242
|
+
if (cFilter)
|
1243
|
+
c.filter = cFilter.filter;
|
1244
|
+
return c;
|
1245
|
+
});
|
1246
|
+
this.nanoTblAfterFilter.emit({ filters: this.filters });
|
1247
|
+
perMark('filter', true);
|
1248
|
+
}
|
1249
|
+
/** Scrolls to the top immediately - used whilst sorting / filtering */
|
1250
|
+
scrollToTop(element) {
|
1251
|
+
const scrollBehaviour = this.scrollParent.style?.scrollBehavior;
|
1252
|
+
const scrollX = this.scrollParent.scrollLeft;
|
1253
|
+
this.scrollParent.style.scrollBehavior = 'auto';
|
1254
|
+
if (this.topAnchorEle && !isInViewport(this.topAnchorEle, 0.1)) {
|
1255
|
+
this.host.scrollIntoView();
|
1256
|
+
}
|
1257
|
+
if (element && !isInViewport(element, 1))
|
1258
|
+
setTimeout(() => element.scrollIntoView({ block: 'start' }), 500);
|
1259
|
+
if (scrollX)
|
1260
|
+
this.scrollParent.scrollLeft = scrollX;
|
1261
|
+
if (scrollBehaviour)
|
1262
|
+
this.scrollParent.style.scrollBehavior = scrollBehaviour;
|
1263
|
+
}
|
1264
|
+
setMeasureElement() {
|
1265
|
+
readTask(() => {
|
1266
|
+
this.measureEle = this.blockElements.find((b) => !b?.classList?.contains(`${CSSNAMESPACE}__inactive`));
|
1267
|
+
});
|
1268
|
+
}
|
1269
|
+
/**
|
1270
|
+
* Sets the initial height on tbody elements that are not active
|
1271
|
+
* These elements have no natural height - on account of all their rows being hidden
|
1272
|
+
* So we just estimate for now
|
1273
|
+
*/
|
1274
|
+
setInitialBlockDimension() {
|
1275
|
+
if (!this.blockElements?.length)
|
1276
|
+
return;
|
1277
|
+
perMark('blockDims');
|
1278
|
+
const finishResizing = new Promise((resolve) => {
|
1279
|
+
readTask(() => {
|
1280
|
+
this.setMeasureElement();
|
1281
|
+
// find the first active block
|
1282
|
+
if (!this.measureEle && !this.unitHeight)
|
1283
|
+
resolve();
|
1284
|
+
this.blockElements.forEach((el, i) => {
|
1285
|
+
if (!el || !el.classList?.contains(`${CSSNAMESPACE}__inactive`)) {
|
1286
|
+
if (i === this.blockElements.length - 1)
|
1287
|
+
resolve();
|
1288
|
+
return;
|
1289
|
+
}
|
1290
|
+
if (i === this.blockElements.length - 1)
|
1291
|
+
resolve();
|
1292
|
+
});
|
1293
|
+
});
|
1294
|
+
});
|
1295
|
+
// we're all finished.
|
1296
|
+
finishResizing.then(() => {
|
1297
|
+
perMark('blockDims', true);
|
1298
|
+
perMark('init', true);
|
1299
|
+
requestAnimationFrame(() => {
|
1300
|
+
this.isReady = true;
|
1301
|
+
});
|
1302
|
+
});
|
1303
|
+
}
|
1304
|
+
/** Apply initial columns settings */
|
1305
|
+
async columnInit() {
|
1306
|
+
this.filters = this.columns
|
1307
|
+
.filter((c) => !!c.filter)
|
1308
|
+
.map((c) => {
|
1309
|
+
const { filter, prop } = c;
|
1310
|
+
return { filter, prop };
|
1311
|
+
});
|
1312
|
+
if (this.searchTerm) {
|
1313
|
+
await this.searchStart();
|
1314
|
+
}
|
1315
|
+
if (this.filters.length) {
|
1316
|
+
await this.filterStart();
|
1317
|
+
}
|
1318
|
+
// apply sort
|
1319
|
+
const col = this.columns.find((c) => !!c.order);
|
1320
|
+
if (!!col) {
|
1321
|
+
await this.sortStart(col.order, col.prop);
|
1322
|
+
}
|
1323
|
+
}
|
1324
|
+
/** Split up all incoming rows into 'blocks' split amongst tbody elements.
|
1325
|
+
* These can then be hidden / shown to improve performance.
|
1326
|
+
*/
|
1327
|
+
setBlocks() {
|
1328
|
+
perMark('setBlocks');
|
1329
|
+
this.activeBlocks = [0, 1, 2];
|
1330
|
+
this.ignoreIO = true;
|
1331
|
+
let i = 1;
|
1332
|
+
const l = this.store.data.state.rows.length;
|
1333
|
+
let rows = [];
|
1334
|
+
const blocks = [];
|
1335
|
+
this.blockHeights = [];
|
1336
|
+
// old skool loop for perf
|
1337
|
+
for (i; i <= l; i++) {
|
1338
|
+
rows.push(this.store.data.state.rows[i - 1]);
|
1339
|
+
if (i % this.perBlock === 0) {
|
1340
|
+
blocks.push({ rows, __uuid: cyrb53(rows.map((b) => b.__uuid).join()) });
|
1341
|
+
rows = [];
|
1342
|
+
}
|
1343
|
+
}
|
1344
|
+
if (rows.length)
|
1345
|
+
blocks.push({ rows, __uuid: cyrb53(rows.map((b) => b.__uuid).join()) });
|
1346
|
+
this.blocks = blocks;
|
1347
|
+
perMark('setBlocks', true);
|
1348
|
+
}
|
1349
|
+
/**
|
1350
|
+
* Returns a block render height.
|
1351
|
+
* If it's currently active - let auto do it's thing
|
1352
|
+
* If we've rendered it before - return that
|
1353
|
+
* If all else fails, let's guess it
|
1354
|
+
* @param blockIndex
|
1355
|
+
* @returns a height string (incl px)
|
1356
|
+
*/
|
1357
|
+
getBlockHeight(blockIndex) {
|
1358
|
+
if (this.activeBlocks.includes(blockIndex))
|
1359
|
+
return undefined;
|
1360
|
+
if (this.blockHeights.length) {
|
1361
|
+
const cachedBlockHeight = this.blockHeights.find((bh) => bh.blockIndex === blockIndex);
|
1362
|
+
if (cachedBlockHeight)
|
1363
|
+
return cachedBlockHeight.height + 'px';
|
1364
|
+
}
|
1365
|
+
const blockLength = this.blocks[blockIndex].rows.length;
|
1366
|
+
if (blockLength === this.perBlock && this.measureHeight) {
|
1367
|
+
return this.measureHeight + 'px';
|
1368
|
+
}
|
1369
|
+
return this.unitHeight ? this.unitHeight * blockLength + 'px' : undefined;
|
1370
|
+
}
|
1371
|
+
/** cache the height for all active blocks for later renders */
|
1372
|
+
setBlockHeight() {
|
1373
|
+
readTask(() => {
|
1374
|
+
this.activeBlocks.forEach((blockIndex) => {
|
1375
|
+
const el = this.blockElements[blockIndex];
|
1376
|
+
if (!el)
|
1377
|
+
return;
|
1378
|
+
const height = el.getBoundingClientRect().height;
|
1379
|
+
// cache height to our block heights array
|
1380
|
+
// for subsequent renders
|
1381
|
+
const fBhI = this.blockHeights.findIndex((bh) => bh.blockIndex === blockIndex);
|
1382
|
+
if (fBhI > 0) {
|
1383
|
+
this.blockHeights[fBhI] = { height, blockIndex };
|
1384
|
+
}
|
1385
|
+
else
|
1386
|
+
this.blockHeights.push({ height, blockIndex });
|
1387
|
+
});
|
1388
|
+
});
|
1389
|
+
}
|
1390
|
+
/** Process slotted content */
|
1391
|
+
processSlots() {
|
1392
|
+
// see if we have slot content
|
1393
|
+
if (!this.caption && !this.host.querySelector('[slot="caption"]')) {
|
1394
|
+
console.error('For accessibility you must set a `caption` prop or use the `caption` slot');
|
1395
|
+
}
|
1396
|
+
}
|
1397
|
+
// Component lifecycle
|
1398
|
+
async componentWillLoad() {
|
1399
|
+
perMark('init');
|
1400
|
+
this.scrollParent = findScrollParent(this.host);
|
1401
|
+
this.store = await generateStore(this.host, this.columns, this.scrollParent);
|
1402
|
+
await this.handleRowsChange();
|
1403
|
+
this.processSlots();
|
1404
|
+
this.store.data.onChange('rows', () => this.setBlocks());
|
1405
|
+
this.setBlocks();
|
1406
|
+
}
|
1407
|
+
connectedCallback() {
|
1408
|
+
this.scrollParent = findScrollParent(this.host);
|
1409
|
+
}
|
1410
|
+
componentDidLoad() {
|
1411
|
+
this.setInitialBlockDimension();
|
1412
|
+
}
|
1413
|
+
componentShouldUpdate(_newVal, _oldVal, stateName) {
|
1414
|
+
// stop double rendering - we use the store for rendering internally
|
1415
|
+
// the public facing props are kept in-sync with the store
|
1416
|
+
// but we don't want it to cause renders
|
1417
|
+
if (['rows', 'columns'].includes(stateName))
|
1418
|
+
return false;
|
1419
|
+
}
|
1420
|
+
componentWillRender() {
|
1421
|
+
perMark('render');
|
1422
|
+
}
|
1423
|
+
componentDidRender() {
|
1424
|
+
requestAnimationFrame(() => (this.ignoreIO = false));
|
1425
|
+
this.setMeasureElement();
|
1426
|
+
perMark('render', true);
|
1427
|
+
}
|
1428
|
+
render() {
|
1429
|
+
this.blockElements = [];
|
1430
|
+
return (h(Host, null, h("div", { class: `${CSSNAMESPACE}__top-anchor`, ref: (a) => (this.topAnchorEle = a) }, "\u00A0"), h("nano-resize-observe", { "aria-labelledby": 'table-caption-' + this.renderId, tabindex: this.type === 'grid' ? '0' : undefined, states: "576w sm, 768w md", class: "sm md", onNanoResizeStateChange: this.handleResizeChange }), h("div", { class: `${CSSNAMESPACE}__wrap sm md`, ref: (div) => (this.tableWrapperEle = div) }, h("nano-progress-bar", { indeterminate: true, class: {
|
1431
|
+
[`${CSSNAMESPACE}__progress-bar`]: true,
|
1432
|
+
[`${CSSNAMESPACE}__progress-bar--show`]: this._loading,
|
1433
|
+
} }), h("table", { role: this.type === 'grid' ? 'grid' : undefined, "aria-readonly": this.type === 'table' ? 'true' : undefined, "aria-rowcount": this.store.data.state.rows.length, "aria-colcount": this.store.config.state.columns.length, class: `${CSSNAMESPACE}`, ref: (tbl) => (this.tableEle = tbl) }, h("caption", { class: {
|
1434
|
+
[`${CSSNAMESPACE}__caption`]: true,
|
1435
|
+
[`${CSSNAMESPACE}__caption--hide`]: !this.showCaption,
|
1436
|
+
}, id: 'table-caption-' + this.renderId }, h("slot", { name: "caption" }, this.caption)), h("thead", null, h(TableHeadFootRow, { rowRenderer: this.headRender, onColumnPinned: this.handleColumnPinned }, this.store.config.state.columns.map((colModel) => [
|
1437
|
+
h(TableColHead, { column: colModel, headRenderer: this.headRender, onColumnSortClick: this.sortStart, onColumnPinned: this.handleColumnPinned, onColumnDrag: this.colDrag, onColumnDrop: this.colDrop, defaults: {
|
1438
|
+
sortable: this.defaultSort,
|
1439
|
+
draggable: this.defaultColDraggable,
|
1440
|
+
} }),
|
1441
|
+
]))), this._loading && !this.blocks.length && (h("tbody", { class: `${CSSNAMESPACE}__active` }, [...Array(10).keys()].map((rowIndex) => (h("tr", null, this.store.config.state.columns.map((_colModel, colIndex) => (h(TableCell, { rowIndex: rowIndex, colIndex: colIndex, nestedContent: h("nano-skeleton", null) })))))))), !this._loading && !this.blocks.length && (h("tr", null, h("th", { class: `${CSSNAMESPACE}__th`, colSpan: this.store.config.state.columns.length }, h("div", { class: "nano-tbl__cell-content nano-tbl__cell-content--no-result" }, h("slot", { name: "no-results" }, "No results found"))))), this.blocks.map((block, blockIndex) => (h("tbody", { key: block.__uuid, id: `tbody-${this.renderId}-${blockIndex}`, ref: (tb) => {
|
1442
|
+
this.blockElements.push(tb);
|
1443
|
+
this.setupBlockIO(tb, blockIndex);
|
1444
|
+
}, class: {
|
1445
|
+
[`${CSSNAMESPACE}__inactive`]: !this.activeBlocks.includes(blockIndex),
|
1446
|
+
[`${CSSNAMESPACE}__active`]: this.activeBlocks.includes(blockIndex),
|
1447
|
+
} }, this.activeBlocks.includes(blockIndex) ? (block.rows.map((row, i) => {
|
1448
|
+
const rowIndex = blockIndex > 0 ? blockIndex * this.perBlock + i : i;
|
1449
|
+
return (h(TableRow, { rowRenderer: this.rowRender, row: row, rowIndex: rowIndex }, this.store.config.state.columns.map((_colModel, colIndex) => (h(TableCell, { rowIndex: rowIndex, colIndex: colIndex })))));
|
1450
|
+
})) : (h("td", { colSpan: this.store.config.state.columns.length, style: {
|
1451
|
+
height: this.getBlockHeight(blockIndex),
|
1452
|
+
} }))))), this.showFooter && (h("tfoot", null, h(TableHeadFootRow, { rowRenderer: this.footRender, onColumnPinned: this.handleColumnPinned }, this.store.config.state.columns.map((colModel) => [
|
1453
|
+
h(TableColHead, { column: colModel, headRenderer: this.footRender, onColumnPinned: this.handleColumnPinned, onColumnSortClick: this.sortStart, defaults: {
|
1454
|
+
sortable: this.defaultSort,
|
1455
|
+
draggable: this.defaultColDraggable,
|
1456
|
+
} }),
|
1457
|
+
]))))), !!this.blocks.length && (h("nano-spinner", { type: "circle", class: {
|
1458
|
+
[`${CSSNAMESPACE}__spinner`]: true,
|
1459
|
+
[`${CSSNAMESPACE}__spinner--show`]: this._loading,
|
1460
|
+
} })))));
|
1461
|
+
}
|
1462
|
+
get host() { return this; }
|
1463
|
+
static get watchers() { return {
|
1464
|
+
"rows": ["handleRowsChange"],
|
1465
|
+
"columns": ["handleColsChange"],
|
1466
|
+
"searchTerm": ["handleSearchTermChange"]
|
1467
|
+
}; }
|
1468
|
+
static get style() { return tableCss; }
|
1469
|
+
}, [4, "nano-table", {
|
1470
|
+
"type": [1],
|
1471
|
+
"caption": [1],
|
1472
|
+
"showCaption": [4, "show-caption"],
|
1473
|
+
"loading": [6148],
|
1474
|
+
"placeholderSize": [2, "placeholder-size"],
|
1475
|
+
"rows": [16],
|
1476
|
+
"columns": [1040],
|
1477
|
+
"headRender": [16],
|
1478
|
+
"rowRender": [16],
|
1479
|
+
"footRender": [16],
|
1480
|
+
"showFooter": [4, "show-footer"],
|
1481
|
+
"perBlock": [2, "per-block"],
|
1482
|
+
"blocksLength": [2050, "blocks-length"],
|
1483
|
+
"searchTerm": [1, "search-term"],
|
1484
|
+
"customFilterFn": [16],
|
1485
|
+
"customSortFn": [16],
|
1486
|
+
"defaultSort": [4, "default-sort"],
|
1487
|
+
"defaultColDraggable": [4, "default-col-draggable"],
|
1488
|
+
"_loading": [32],
|
1489
|
+
"blocks": [32],
|
1490
|
+
"activeBlocks": [32],
|
1491
|
+
"measureHeight": [32],
|
1492
|
+
"resetSorting": [64],
|
1493
|
+
"addSort": [64],
|
1494
|
+
"resetFilters": [64],
|
1495
|
+
"addFilters": [64],
|
1496
|
+
"removeFilters": [64]
|
1497
|
+
}, [[9, "scroll", "scrollListener"]]]);
|
1498
|
+
function defineCustomElement() {
|
1499
|
+
if (typeof customElements === "undefined") {
|
1500
|
+
return;
|
1501
|
+
}
|
1502
|
+
const components = ["nano-table", "nano-icon", "nano-progress-bar", "nano-resize-observe", "nano-skeleton", "nano-spinner"];
|
1503
|
+
components.forEach(tagName => { switch (tagName) {
|
1504
|
+
case "nano-table":
|
1505
|
+
if (!customElements.get(tagName)) {
|
1506
|
+
customElements.define(tagName, Table);
|
1507
|
+
}
|
1508
|
+
break;
|
1509
|
+
case "nano-icon":
|
1510
|
+
if (!customElements.get(tagName)) {
|
1511
|
+
defineCustomElement$5();
|
1512
|
+
}
|
1513
|
+
break;
|
1514
|
+
case "nano-progress-bar":
|
1515
|
+
if (!customElements.get(tagName)) {
|
1516
|
+
defineCustomElement$4();
|
1517
|
+
}
|
1518
|
+
break;
|
1519
|
+
case "nano-resize-observe":
|
1520
|
+
if (!customElements.get(tagName)) {
|
1521
|
+
defineCustomElement$3();
|
1522
|
+
}
|
1523
|
+
break;
|
1524
|
+
case "nano-skeleton":
|
1525
|
+
if (!customElements.get(tagName)) {
|
1526
|
+
defineCustomElement$2();
|
1527
|
+
}
|
1528
|
+
break;
|
1529
|
+
case "nano-spinner":
|
1530
|
+
if (!customElements.get(tagName)) {
|
1531
|
+
defineCustomElement$1();
|
1532
|
+
}
|
1533
|
+
break;
|
1534
|
+
} });
|
1535
|
+
}
|
1536
|
+
|
1537
|
+
export { Table as T, createWorker as c, defineCustomElement as d };
|
1538
|
+
|
1539
|
+
//# sourceMappingURL=table.js.map
|