@nanoporetech-digital/components 3.4.0 → 3.5.1

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.
Files changed (101) hide show
  1. package/CHANGELOG.md +23 -0
  2. package/dist/cjs/index-41582c2a.js +16 -12
  3. package/dist/cjs/index.cjs.js.map +1 -1
  4. package/dist/cjs/loader.cjs.js +1 -1
  5. package/dist/cjs/nano-components.cjs.js +1 -1
  6. package/dist/cjs/nano-demo.cjs.entry.js +291 -0
  7. package/dist/cjs/nano-demo.cjs.entry.js.map +1 -0
  8. package/dist/cjs/{nano-table-b9cdafab.js → nano-table-067e0c12.js} +486 -202
  9. package/dist/cjs/nano-table-067e0c12.js.map +1 -0
  10. package/dist/cjs/nano-table.cjs.entry.js +1 -1
  11. package/dist/cjs/{table.worker-291904c9.js → table.worker-a4d75c46.js} +3 -3
  12. package/dist/cjs/table.worker-a4d75c46.js.map +1 -0
  13. package/dist/cjs/table.worker-e9fb087e.js +4 -0
  14. package/dist/collection/collection-manifest.json +1 -0
  15. package/dist/collection/components/alert/alert.helpers.js.map +1 -1
  16. package/dist/collection/components/demo/demo.js +284 -0
  17. package/dist/collection/components/demo/demo.js.map +1 -0
  18. package/dist/collection/components/dialog/dialog.helpers.js.map +1 -1
  19. package/dist/collection/components/table/table-interface.js.map +1 -1
  20. package/dist/collection/components/table/table.cell.js +65 -0
  21. package/dist/collection/components/table/table.cell.js.map +1 -0
  22. package/dist/collection/components/table/table.css +98 -20
  23. package/dist/collection/components/table/table.header.js +156 -0
  24. package/dist/collection/components/table/table.header.js.map +1 -0
  25. package/dist/collection/components/table/table.js +170 -18
  26. package/dist/collection/components/table/table.js.map +1 -1
  27. package/dist/collection/components/table/table.row.js +113 -0
  28. package/dist/collection/components/table/table.row.js.map +1 -0
  29. package/dist/collection/components/table/table.store.js +46 -9
  30. package/dist/collection/components/table/table.store.js.map +1 -1
  31. package/dist/collection/components/table/table.utils.js +230 -0
  32. package/dist/collection/components/table/table.utils.js.map +1 -1
  33. package/dist/collection/components/table/table.worker.js +1 -0
  34. package/dist/collection/components/table/table.worker.js.map +1 -1
  35. package/dist/components/index.d.ts +1 -0
  36. package/dist/components/index.js +1 -0
  37. package/dist/components/index.js.map +1 -1
  38. package/dist/components/nano-demo.d.ts +11 -0
  39. package/dist/components/nano-demo.js +341 -0
  40. package/dist/components/nano-demo.js.map +1 -0
  41. package/dist/components/nano-table.js +2 -1247
  42. package/dist/components/nano-table.js.map +1 -1
  43. package/dist/components/table.js +1539 -0
  44. package/dist/components/table.js.map +1 -0
  45. package/dist/components/table.worker.js +2 -2
  46. package/dist/custom-elements/index.d.ts +6 -0
  47. package/dist/custom-elements/index.js +772 -204
  48. package/dist/custom-elements/index.js.map +1 -1
  49. package/dist/esm/index-3c280603.js +16 -12
  50. package/dist/esm/index.js.map +1 -1
  51. package/dist/esm/loader.js +1 -1
  52. package/dist/esm/nano-components.js +1 -1
  53. package/dist/esm/nano-demo.entry.js +287 -0
  54. package/dist/esm/nano-demo.entry.js.map +1 -0
  55. package/dist/esm/{nano-table-c8ef2276.js → nano-table-95921f46.js} +487 -203
  56. package/dist/esm/nano-table-95921f46.js.map +1 -0
  57. package/dist/esm/nano-table.entry.js +1 -1
  58. package/dist/esm/{table.worker-65438fa0.js → table.worker-769f1441.js} +3 -3
  59. package/dist/esm/table.worker-769f1441.js.map +1 -0
  60. package/dist/esm/table.worker-e9fb087e.js +4 -0
  61. package/dist/nano-components/index.esm.js.map +1 -1
  62. package/dist/nano-components/nano-components.esm.js +1 -1
  63. package/dist/nano-components/nano-components.esm.js.map +1 -1
  64. package/dist/nano-components/p-26905bca.js +5 -0
  65. package/dist/nano-components/{p-14218d23.entry.js.map → p-26905bca.js.map} +0 -0
  66. package/dist/nano-components/p-85cfb0af.entry.js +5 -0
  67. package/dist/nano-components/p-85cfb0af.entry.js.map +1 -0
  68. package/dist/nano-components/p-8fc2a38d.js +5 -0
  69. package/dist/nano-components/p-8fc2a38d.js.map +1 -0
  70. package/dist/nano-components/p-e9fb087e.js +4 -0
  71. package/dist/nano-components/{p-14218d23.entry.js → p-fb12a45d.entry.js} +2 -2
  72. package/dist/nano-components/{p-fe1f8360.js.map → p-fb12a45d.entry.js.map} +0 -0
  73. package/dist/types/components/alert/alert.helpers.d.ts +1 -1
  74. package/dist/types/components/demo/demo.d.ts +6 -0
  75. package/dist/types/components/dialog/dialog.helpers.d.ts +1 -1
  76. package/dist/types/components/table/table-interface.d.ts +38 -23
  77. package/dist/types/components/table/table.cell.d.ts +18 -0
  78. package/dist/types/components/table/table.d.ts +36 -1
  79. package/dist/types/components/table/table.header.d.ts +16 -0
  80. package/dist/types/components/table/table.row.d.ts +15 -0
  81. package/dist/types/components/table/table.utils.d.ts +99 -0
  82. package/dist/types/components.d.ts +50 -0
  83. package/docs-json.json +124 -3
  84. package/docs-vscode.json +17 -1
  85. package/package.json +3 -3
  86. package/dist/cjs/nano-table-b9cdafab.js.map +0 -1
  87. package/dist/cjs/table.worker-1fd13775.js +0 -4
  88. package/dist/cjs/table.worker-291904c9.js.map +0 -1
  89. package/dist/collection/components/table/table.children.js +0 -224
  90. package/dist/collection/components/table/table.children.js.map +0 -1
  91. package/dist/collection/components/table/table.service.js +0 -121
  92. package/dist/collection/components/table/table.service.js.map +0 -1
  93. package/dist/esm/nano-table-c8ef2276.js.map +0 -1
  94. package/dist/esm/table.worker-1fd13775.js +0 -4
  95. package/dist/esm/table.worker-65438fa0.js.map +0 -1
  96. package/dist/nano-components/p-1fd13775.js +0 -4
  97. package/dist/nano-components/p-f9349146.js +0 -5
  98. package/dist/nano-components/p-f9349146.js.map +0 -1
  99. package/dist/nano-components/p-fe1f8360.js +0 -5
  100. package/dist/types/components/table/table.children.d.ts +0 -30
  101. package/dist/types/components/table/table.service.d.ts +0 -42
@@ -1,1256 +1,11 @@
1
1
  /*!
2
2
  * Web Components for Nanopore digital Web Apps
3
3
  */
4
- import { consoleError, h, Fragment, getElement, getRenderingRef, 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$6 } from './icon.js';
9
- import { d as defineCustomElement$5 } from './progress-bar.js';
10
- import { d as defineCustomElement$4 } from './resize-observe.js';
11
- import { d as defineCustomElement$3 } from './skeleton.js';
12
- import { d as defineCustomElement$2 } 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 storeSetData(host, rows) {
173
- const store = stores.get(host);
174
- if (!store)
175
- return;
176
- // augment data with some internal props
177
- rows = rows.map((row, i) => ({
178
- ...row,
179
- __index: i,
180
- __uuid: cyrb53(Object.values(row).join()),
181
- }));
182
- store.data.state.rows = rows;
183
- if (store.general.state.workerId)
184
- return syncDataToWorker(store.general.state.workerId, rows);
185
- }
186
- function storeSetConfig(host, columns) {
187
- const store = stores.get(host);
188
- if (!store)
189
- return;
190
- store.config.state.columns = columns;
191
- if (store.general.state.workerId)
192
- return syncConfigToWorker(store.general.state.workerId, colsToWorker(columns));
193
- }
194
- async function storeSearch(host, term) {
195
- const store = stores.get(host);
196
- if (!store || !store.general.state.workerId)
197
- return;
198
- try {
199
- store.data.state.rows = await workerSearch(store.general.state.workerId, term);
200
- }
201
- catch (e) {
202
- console.log(e);
203
- }
204
- }
205
- async function storeFilter(host, filters) {
206
- const store = stores.get(host);
207
- if (!store || !store.general.state.workerId)
208
- return;
209
- try {
210
- store.data.state.rows = await workerFilter(store.general.state.workerId, filters);
211
- }
212
- catch (e) {
213
- console.log(e);
214
- }
215
- }
216
- async function storeSort(host, prop, order) {
217
- const store = stores.get(host);
218
- if (!store || !store.general.state.workerId)
219
- return;
220
- try {
221
- store.data.state.rows = await workerSort(store.general.state.workerId, prop, order);
222
- }
223
- catch (e) {
224
- console.log(e);
225
- }
226
- }
227
-
228
- /**
229
- * Get a model object for custom cell / property renderers.
230
- * @param rowIndex - the current row index being rendered
231
- * @param colIndex - the current column index being rendered
232
- * @returns a model object which will be passed to custom renderers
233
- */
234
- function colDataModel(rowIndex, colIndex) {
235
- const store = fetchStores();
236
- const columns = store.config.state.columns;
237
- const rows = store.data.state.rows;
238
- const column = columns[colIndex];
239
- const prop = column?.prop;
240
- const rowModel = rows[rowIndex];
241
- const cellModel = rowModel[columns[colIndex].prop];
242
- return {
243
- prop,
244
- cellModel,
245
- column,
246
- rowIndex,
247
- rowModel,
248
- };
249
- }
250
- /**
251
- * Get a model object for custom row renderers.
252
- * @param rowIndex
253
- * @returns a model object passed to custom row renderers
254
- */
255
- function rowDataModel(rowIndex) {
256
- const store = fetchStores();
257
- const rows = store.data.state.rows;
258
- const row = rows[rowIndex];
259
- return {
260
- row,
261
- rowIndex,
262
- };
263
- }
264
- /**
265
- * Merges 2 objects of properties together
266
- * @param current - property object
267
- * @param extra - additional object property
268
- * @returns - merged properties that can be applied to a node
269
- */
270
- function mergeProperties(current, extra) {
271
- if (!extra)
272
- return current;
273
- // top level merge
274
- const props = { ...extra, ...current };
275
- // deeper merge
276
- // merge classes maps or strings
277
- if (extra.class) {
278
- if (typeof extra.class === 'object' && typeof props.class === 'object') {
279
- props.class = { ...extra.class, ...props.class };
280
- }
281
- else if (typeof extra.class === 'string' &&
282
- typeof props.class === 'object') {
283
- props.class[extra.class] = true;
284
- }
285
- else if (typeof props.class === 'string') {
286
- props.class += ' ' + extra.class;
287
- }
288
- }
289
- // merge style
290
- if (extra.style) {
291
- props.style = { ...extra.style, ...props.style };
292
- }
293
- return props;
294
- }
295
- /**
296
- * Returns the current nano-table's stores.
297
- * @returns the current nano-table stores
298
- */
299
- function fetchStores() {
300
- return getStore(getElement(getRenderingRef()));
301
- }
302
- /**
303
- * Merges any defined cell properties with properties
304
- * required by `nano-table` functionality
305
- * @param rowIndex - the current row index being rendered
306
- * @param colIndex = the current column index being rendered
307
- * @param defaultProps - default properties required by `nano-table`
308
- * @returns - the merged properties that will be applied to a node
309
- */
310
- function mergeCellProperties(rowIndex, colIndex, defaultProps) {
311
- const props = { ...defaultProps };
312
- const extraPropsFunc = fetchStores().config.state.columns[colIndex]?.cellProperties;
313
- if (!extraPropsFunc)
314
- return props;
315
- const data = colDataModel(rowIndex, colIndex);
316
- const extra = extraPropsFunc(data);
317
- if (!extra)
318
- return props;
319
- return mergeProperties(props, extra);
320
- }
321
- /**
322
- * Renders a cell using a custom renderer if set.
323
- * @param rowIndex - the current row index being rendered
324
- * @param colIndex - the current column index being rendered
325
- * @returns - a JSX node
326
- */
327
- function cellRender(rowIndex, colIndex) {
328
- const store = fetchStores();
329
- const columns = store.config.state.columns;
330
- const tpl = columns[colIndex]?.cellTemplate;
331
- const model = colDataModel(rowIndex, colIndex);
332
- return tpl ? (tpl(h, model)) : (h(Fragment, null, model.cellModel.toString()));
333
- }
334
- /**
335
- * Renders a table header (within a thead) using a custom template if set.
336
- * @param col - the current column config object
337
- * @returns - a JSX node
338
- */
339
- function colheadFootRender(col) {
340
- const tpl = col?.colTemplate;
341
- return tpl ? (tpl(h, col)) : (h(Fragment, null, col.title));
342
- }
343
-
344
- function baseClasses(type, vPinned, toString = false) {
345
- const classes = {
346
- [`${CSSNAMESPACE}__${type}`]: true,
347
- [`${CSSNAMESPACE}__pin`]: !!vPinned,
348
- [`${CSSNAMESPACE}__pin--top`]: vPinned === 'top',
349
- [`${CSSNAMESPACE}__pin--bottom`]: vPinned === 'bottom',
350
- };
351
- if (toString) {
352
- let classString = '';
353
- Object.entries(classes).forEach(([className, on]) => {
354
- if (on)
355
- classString += className + ' ';
356
- });
357
- return classString;
358
- }
359
- return classes;
360
- }
361
- const stickyHIOs = new WeakMap();
362
- const stickyVIOs = new WeakMap();
363
- function addHObserver(el, pos, cb) {
364
- if (stickyHIOs.get(el))
365
- return;
366
- const store = fetchStores();
367
- const root = store.general.state.scrollParent;
368
- const observer = new IntersectionObserver(([e]) => {
369
- const rootBounds = e.rootBounds || document.scrollingElement.getBoundingClientRect();
370
- const positions = {};
371
- if (pos === 'start') {
372
- positions.start =
373
- e.boundingClientRect.x - (rootBounds.x + root.scrollLeft) < 0 &&
374
- !e.isIntersecting;
375
- }
376
- if (pos === 'end') {
377
- // console.log('refe', store.general.state.host)
378
- // TODO - sort these out for RtL
379
- positions.end =
380
- e.boundingClientRect.right > e.boundingClientRect.width &&
381
- !e.isIntersecting;
382
- }
383
- cb(positions);
384
- }, {
385
- threshold: [1],
386
- rootMargin: '1px 0px 100px 0px',
387
- root: root === document.scrollingElement ? null : root,
388
- });
389
- stickyHIOs.set(el, observer);
390
- // dirty fix - wait a tick 'cos nano-size-observer isn't always ready in-time
391
- setTimeout(() => observer.observe(el), 300);
392
- }
393
- function addVObserver(el, pos, cb) {
394
- if (stickyVIOs.get(el))
395
- return;
396
- const store = fetchStores();
397
- const root = store.general.state.scrollParent;
398
- const observer = new IntersectionObserver(([e]) => {
399
- const rootBounds = e.rootBounds || document.scrollingElement.getBoundingClientRect();
400
- const positions = {};
401
- if (pos === 'top') {
402
- positions.top =
403
- e.boundingClientRect.y - (rootBounds.y + root.scrollTop) < 0 &&
404
- !e.isIntersecting;
405
- }
406
- if (pos === 'bottom')
407
- positions.bottom =
408
- e.boundingClientRect.height > e.intersectionRect.height &&
409
- !e.isIntersecting;
410
- cb(positions);
411
- }, {
412
- threshold: [1],
413
- rootMargin: '0px 100px 0px 100px',
414
- root: root === document.scrollingElement ? null : root,
415
- });
416
- stickyVIOs.set(el, observer);
417
- requestAnimationFrame(() => observer.observe(el));
418
- }
419
- const TableRow = ({ rowRenderer, rowIndex, row }, children) => {
420
- let extraProps = {};
421
- if (!row) {
422
- const model = rowDataModel(rowIndex);
423
- row = model.row;
424
- }
425
- if (rowRenderer?.rowProperties) {
426
- extraProps =
427
- rowRenderer.rowProperties({ row: row, rowIndex }) || extraProps;
428
- }
429
- let pinned;
430
- if (rowRenderer?.pinned && typeof rowRenderer.pinned === 'function') {
431
- pinned = rowRenderer.pinned();
432
- }
433
- const baseProps = { class: baseClasses('tr', pinned) };
434
- const props = extraProps ? mergeProperties(baseProps, extraProps) : baseProps;
435
- return (h("tr", { ...props, key: row.__uuid }, children));
436
- };
437
- const TableHeadFootRow = ({ rowRenderer, onColumnPinned }, children, utils) => {
438
- let extraProps = {};
439
- if (rowRenderer.rowProperties) {
440
- extraProps = rowRenderer.rowProperties() || {};
441
- }
442
- const pinned = rowRenderer.pinned || null;
443
- const baseProps = { class: baseClasses('tr', null) };
444
- const props = extraProps ? mergeProperties(baseProps, extraProps) : baseProps;
445
- const tpl = rowRenderer?.template;
446
- if (tpl) {
447
- let toRender = tpl(h, {
448
- renderedRow: h("tr", { ...props }, children),
449
- });
450
- if (Array.isArray(toRender)) {
451
- toRender = utils.map(toRender, (node) => {
452
- if (node.vtag === 'tr') {
453
- node.vattrs = mergeProperties({ class: baseClasses('tr', null, true) }, node.vattrs);
454
- if (!!node.vchildren) {
455
- node.vchildren = utils.map(node.vchildren, (cNode) => {
456
- if (['td', 'th'].includes(cNode.vtag.toString())) {
457
- cNode.vattrs = mergeProperties({
458
- class: baseClasses(cNode.vtag.toString(), pinned, true),
459
- ref: (th) => {
460
- addVObserver(th, pinned, onColumnPinned);
461
- },
462
- }, cNode.vattrs);
463
- }
464
- return cNode;
465
- });
466
- }
467
- }
468
- return node;
469
- });
470
- }
471
- return toRender;
472
- }
473
- return h("tr", { ...props }, children);
474
- };
475
- const TableColHead = ({ column, headRenderer, onColumnOrderClick, onColumnPinned, }) => {
476
- function handleColumnOrderClick(e) {
477
- let order;
478
- switch (column.order) {
479
- case 'asc':
480
- order = 'desc';
481
- break;
482
- case 'desc':
483
- order = null;
484
- break;
485
- default:
486
- order = 'asc';
487
- }
488
- onColumnOrderClick(order, column.prop, e.target.closest('th'));
489
- }
490
- let extraProps = {};
491
- if (column.columnProperties) {
492
- extraProps = column.columnProperties(column) || extraProps;
493
- }
494
- const baseProps = {
495
- class: {
496
- ...baseClasses('th', headRenderer?.pinned),
497
- [`${CSSNAMESPACE}__pin--start`]: column.pinned === 'start',
498
- [`${CSSNAMESPACE}__pin--end`]: column.pinned === 'end',
499
- [`${CSSNAMESPACE}__ordered`]: !!column.order,
500
- [`${CSSNAMESPACE}__filtered`]: !!column.filter,
501
- },
502
- };
503
- let props = extraProps ? mergeProperties(baseProps, extraProps) : baseProps;
504
- const content = colheadFootRender(column);
505
- if (!content)
506
- return h(Fragment, null);
507
- props =
508
- Number(props.colspan) > 1
509
- ? { ...props, scope: 'colgroup' }
510
- : { ...props, scope: 'col' };
511
- if (column.sortable !== false) {
512
- return (h("th", { ...props, "aria-sort": column.order
513
- ? column.order === 'asc'
514
- ? 'ascending'
515
- : 'descending'
516
- : 'none', ref: (th) => {
517
- if (['end', 'start'].includes(column.pinned))
518
- addHObserver(th, column.pinned, onColumnPinned);
519
- if (['top', 'bottom'].includes(headRenderer.pinned))
520
- addVObserver(th, headRenderer.pinned, onColumnPinned);
521
- } },
522
- h("button", { class: {
523
- [`${CSSNAMESPACE}__order-btn`]: true,
524
- [`${CSSNAMESPACE}__cell-content`]: true,
525
- }, onClick: handleColumnOrderClick },
526
- colheadFootRender(column),
527
- !!column.filter && h("nano-icon", { name: "light/filter" }),
528
- !!column.order &&
529
- (column.order === 'desc' ? (h("nano-icon", { name: "solid/long-arrow-down" })) : (h("nano-icon", { name: "solid/long-arrow-up" }))),
530
- h("div", { class: `${CSSNAMESPACE}__status-icons` },
531
- h("nano-icon", { name: "light/chevron-down" })))));
532
- }
533
- return (h("th", { ...props, ref: (th) => {
534
- if (['end', 'start'].includes(column.pinned))
535
- addHObserver(th, column.pinned, onColumnPinned);
536
- if (['top', 'bottom'].includes(headRenderer.pinned))
537
- addVObserver(th, headRenderer.pinned, onColumnPinned);
538
- } },
539
- h("div", { class: `${CSSNAMESPACE}__cell-content` },
540
- colheadFootRender(column),
541
- !!column.filter && h("nano-icon", { name: "light/bars-filter" }))));
542
- };
543
- const TableCell = ({ rowIndex, colIndex, nestedContent, }) => {
544
- const content = nestedContent || cellRender(rowIndex, colIndex);
545
- if (!content)
546
- return h(Fragment, null);
547
- const store = fetchStores();
548
- const column = store.config.state.columns[colIndex];
549
- let CellType = 'td';
550
- let props = mergeCellProperties(rowIndex, colIndex, {
551
- class: {
552
- [`${CSSNAMESPACE}__td`]: true,
553
- [`${CSSNAMESPACE}__ordered`]: !!column.order,
554
- [`${CSSNAMESPACE}__pin`]: !!column.pinned,
555
- [`${CSSNAMESPACE}__pin--start`]: column.pinned === 'start',
556
- [`${CSSNAMESPACE}__pin--end`]: column.pinned === 'end',
557
- },
558
- });
559
- if (column.rowHeader) {
560
- props =
561
- Number(props.rowspan) > 1
562
- ? { ...props, scope: 'rowgroup' }
563
- : { ...props, scope: 'row' };
564
- CellType = 'th';
565
- }
566
- return (h(CellType
567
- // role="gridcell"
568
- , { ...props },
569
- h("div", { class: `${CSSNAMESPACE}__cell-content` }, content)));
570
- };
571
-
572
- const detectScrollSpeed = (() => {
573
- let lastPos;
574
- let newPos;
575
- let timer;
576
- let delta;
577
- const delay = 60; // in "ms" (higher means lower fidelity )
578
- const clear = () => {
579
- lastPos = null;
580
- delta = 0;
581
- };
582
- clear();
583
- return () => {
584
- newPos = window.scrollY;
585
- if (lastPos != null)
586
- delta = newPos - lastPos;
587
- lastPos = newPos;
588
- window.clearTimeout(timer);
589
- timer = window.setTimeout(clear, delay);
590
- return delta;
591
- };
592
- })();
593
- function findScrollParent(element) {
594
- let style = getComputedStyle(element);
595
- const excludeStaticParent = style.position === 'absolute';
596
- const overflowRegex = /(auto|scroll)/;
597
- if (style.position === 'fixed')
598
- return document.documentElement;
599
- for (let parent = element; (parent = parent.parentElement);) {
600
- style = getComputedStyle(parent);
601
- if (excludeStaticParent && style.position === 'static') {
602
- continue;
603
- }
604
- if (overflowRegex.test(style.overflow + style.overflowY + style.overflowX))
605
- return parent;
606
- }
607
- return document.documentElement;
608
- }
609
- function isInViewport(el, percentVisible = 100) {
610
- const r = el.getBoundingClientRect();
611
- const windowHeight = window.innerHeight || document.documentElement.clientHeight;
612
- if (!r.bottom &&
613
- !r.top &&
614
- !r.left &&
615
- !r.right &&
616
- !r.height &&
617
- !r.width &&
618
- !r.x &&
619
- !r.y)
620
- return false;
621
- return !(Math.floor(100 - ((r.top >= 0 ? 0 : r.top) / +-r.height) * 100) <
622
- percentVisible ||
623
- Math.floor(100 - ((r.bottom - windowHeight) / r.height) * 100) <
624
- percentVisible);
625
- }
626
-
627
- const tableCss = ":host{box-sizing:border-box}*,*::before,*::after{box-sizing:border-box}[hidden]{display:none !important}@charset \"UTF-8\";nano-table{display:block;width:100%;--max-col-width:200px;--color:var(--nano-color-mediumgrey, #68767e);--font-size:0.87rem;--cell-line-height:1.5;--thead-font-size:0.95rem;--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(--th-padding-top) var(--th-padding-bottom);--foot-th-padding-h:var(--th-padding-start) var(--th-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__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{font-weight:800;background:rgba(var(--head-bg-rgb), 90%);font-size:var(--thead-font-size);border-block-start:none}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{font-weight:800;border-block-end: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 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}.sm .nano-tbl th[scope=row]{max-width:65vw !important}.nano-tbl__pin{position:sticky;z-index:1}.nano-tbl__pin--start{inset-inline:-1px auto;transition:max-width 0.25s}.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}.sm .nano-tbl__pinned--start .nano-tbl__pin--start{max-width:var(--max-col-width)}.nano-tbl__pinned--start .nano-tbl__pin--start::after{opacity:1}.nano-tbl__pin--end{}.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}.nano-tbl__pinned--top .nano-tbl__pin--top{z-index:4}.nano-tbl__pin--bottom{inset-block:auto -1px}.nano-tbl__pinned--bottom .nano-tbl__pin--bottom{z-index:5;border-block-start:var(--border-tint-style) !important}.nano-tbl__pinned--top.nano-tbl__pinned--start .nano-tbl__pin--top.nano-tbl__pin--start{z-index:6}.nano-tbl__pinned--top.nano-tbl__pinned--end .nano-tbl__pin--top.nano-tbl__pin--end{z-index:6}.nano-tbl__pinned--bottom.nano-tbl__pinned--start .nano-tbl__pin--bottom.nano-tbl__pin--start{z-index:6}.nano-tbl__pinned--bottom.nano-tbl__pinned--end .nano-tbl__pin--bottom.nano-tbl__pin--end{z-index:6}.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 .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)}";
628
-
629
- function perMark(name, end = false) {
630
- if (!Build.isDev)
631
- return;
632
- if (end) {
633
- performance?.mark('end' + name);
634
- performance?.measure(name, 'start' + name, 'end' + name);
635
- const entries = performance?.getEntriesByName(name);
636
- console.log(entries[entries.length ? entries.length - 1 : 0]);
637
- }
638
- else {
639
- performance?.mark('start' + name);
640
- }
641
- }
642
- let id = 0;
643
- const Table = /*@__PURE__*/ proxyCustomElement(class extends HTMLElement {
644
- constructor() {
645
- super();
646
- this.__registerHost();
647
- this.nanoTblBlockRendered = createEvent(this, "nanoTblBlockRendered", 7);
648
- this.nanoTblReady = createEvent(this, "nanoTblReady", 7);
649
- this.nanoTblBeforeSort = createEvent(this, "nanoTblBeforeSort", 7);
650
- this.nanoTblAfterSort = createEvent(this, "nanoTblAfterSort", 7);
651
- this.nanoTblBeforeFilter = createEvent(this, "nanoTblBeforeFilter", 7);
652
- this.nanoTblAfterFilter = createEvent(this, "nanoTblAfterFilter", 7);
653
- this.nanoTblBeforeSearch = createEvent(this, "nanoTblBeforeSearch", 7);
654
- this.nanoTblAfterSearch = createEvent(this, "nanoTblAfterSearch", 7);
655
- this.debounceSetLoading = (l) => {
656
- this._loading = !!this.store.data.state.rows.length ? l : true;
657
- };
658
- this.renderId = 'tbl-' + id++;
659
- this.filters = [];
660
- this.currentFilters = '';
661
- this.currentSort = '';
662
- this.blockIos = new WeakMap();
663
- this.blockHeights = [];
664
- this.unitHeight = 0;
665
- // Scroll / IO used for hiding / showing blocks
666
- this.ignoreIO = true;
667
- this._isReady = false;
668
- /**
669
- * Start a sort - can be cancelled by `preventDefault`
670
- * @param order - column order
671
- * @param column - column config object
672
- * @returns A promise
673
- */
674
- this.sortStart = async (order, column, element) => {
675
- // did order change?
676
- if (this.currentSort === order + ':' + column)
677
- return;
678
- this.loading = true;
679
- const sortEvent = this.nanoTblBeforeSort.emit({ column: column, order });
680
- if (sortEvent.defaultPrevented)
681
- return;
682
- perMark('sort');
683
- this.currentSort = order + ':' + column;
684
- // doesn't make sense to leave user in place for a sort
685
- this.scrollToTop(element);
686
- if (this.customSortFn) {
687
- try {
688
- await this.customSortFn(column, order);
689
- this.sortComplete(order, column);
690
- }
691
- catch (e) {
692
- console.warn('custom sort failed', e);
693
- }
694
- return;
695
- }
696
- try {
697
- await storeSort(this.host, column, order);
698
- this.sortComplete(order, column);
699
- }
700
- catch (e) {
701
- console.warn('sort failed', e);
702
- this.currentSort = '';
703
- }
704
- finally {
705
- this.loading = false;
706
- }
707
- };
708
- /**
709
- * Attaches an intersection observer to each rendered tbody element
710
- * shows / hides intersecting blocks' and sets heights for when they're hidden
711
- * @param el - the tbody element to observe
712
- * @param blockIndex - the rendering tbody we're attaching the IO to
713
- */
714
- this.setupBlockIO = (el, blockIndex) => {
715
- if (!el || this.blockIos.has(el))
716
- return;
717
- const blockIo = new IntersectionObserver(([ioEntry]) => {
718
- if (this.ignoreIO)
719
- return;
720
- if (ioEntry.isIntersecting) {
721
- // This is a bit gross
722
- // The Intersection Observer (IO) fires in an incorrect order when the scrolling is very fast
723
- // i.e. we go past blocks 3, 2, 1 and land on 0, but 3 can fire as 'intersecting' after 0.
724
- // To fix that, we check - for realsies - if the block IS visible.
725
- // BUT that test is not as sensitive to a block being visible via the IO,
726
- // so doesn't always fire if scrolling slowly
727
- // *sigh*
728
- readTask(() => {
729
- if (this.scrollSpeed < 100 || isInViewport(el, 0.01)) {
730
- this.activeBlocks = [
731
- blockIndex,
732
- blockIndex + 1,
733
- Math.max(0, blockIndex - 1),
734
- ];
735
- this.nanoTblBlockRendered.emit({
736
- block: blockIndex,
737
- totalBlocks: this.blockElements.length,
738
- });
739
- requestAnimationFrame(() => this.setBlockHeight());
740
- }
741
- });
742
- }
743
- }, {
744
- threshold: [0],
745
- root: this.scrollParent === document.scrollingElement
746
- ? null
747
- : this.scrollParent,
748
- });
749
- blockIo.observe(el);
750
- this.blockIos.set(el, blockIo);
751
- };
752
- this.handleColumnPinned = (positions) => {
753
- Object.entries(positions).forEach(([key, applied]) => {
754
- this.tableEle.classList.toggle(`${CSSNAMESPACE}__pinned--${key}`, applied);
755
- });
756
- };
757
- this.type = 'table';
758
- this.caption = undefined;
759
- this.showCaption = false;
760
- this._loading = false;
761
- this.placeholderSize = 5;
762
- this.rows = undefined;
763
- this.columns = [];
764
- this.headRender = { pinned: 'top' };
765
- this.rowRender = undefined;
766
- this.footRender = { pinned: 'bottom' };
767
- this.showFooter = false;
768
- this.perBlock = 60;
769
- this.searchTerm = undefined;
770
- this.customFilterFn = undefined;
771
- this.customSortFn = undefined;
772
- this.blocks = [];
773
- this.activeBlocks = [0, 1, 2];
774
- this.measureHeight = 0;
775
- this.debounceSetLoading = debounce(this.debounceSetLoading.bind(this), 50);
776
- }
777
- /** Will show a loading state when true.
778
- * Will be shown automatically if `rows` is a promise waiting to resolve
779
- * or when performing custom filtering or sorting */
780
- get loading() {
781
- return this._loading;
782
- }
783
- set loading(l) {
784
- this.debounceSetLoading(l);
785
- }
786
- handleRowsChange() {
787
- if (!this.rows) {
788
- this.loading = true;
789
- return;
790
- }
791
- this.loading = true;
792
- Promise.resolve(this.rows).then(async (rows) => {
793
- await storeSetData(this.host, rows);
794
- // reset everything
795
- this.currentFilters = '';
796
- this.currentSort = '';
797
- await this.columnInit();
798
- if (!this.isReady) {
799
- this.setInitialBlockDimension();
800
- }
801
- this.loading = false;
802
- });
803
- }
804
- async handleColsChange() {
805
- await storeSetConfig(this.host, this.columns);
806
- if (this.isReady)
807
- this.columnInit();
808
- }
809
- /** The number of total blocks currently rendered in the table. @readonly */
810
- get blocksLength() {
811
- return this.blocks.length;
812
- }
813
- handleSearchTermChange() {
814
- this.searchStart();
815
- }
816
- /** Remove any column sorts currently applied
817
- * @returns a promise which resolves when complete */
818
- async resetSorting() {
819
- const col = this.columns.find((c) => !!c.order);
820
- if (!col)
821
- return;
822
- return this.sortStart(null, col.prop);
823
- }
824
- /** Apply a sort on a column
825
- * @returns a promise which resolves when complete */
826
- async addSort(column, order) {
827
- const col = this.columns.find((c) => c.prop === column);
828
- if (!col)
829
- throw 'Cannot find column with ' + column;
830
- return this.sortStart(order, col.prop);
831
- }
832
- /** Remove any column filters currently applied
833
- * @returns a promise which resolves when complete */
834
- async resetFilters() {
835
- this.filters = [];
836
- return this.filterStart();
837
- }
838
- /** Apply a filter on a column
839
- * @param filters - the filters to apply
840
- * @param additive - if true, will add the filters to any currently applied
841
- * @returns a promise which resolves when complete
842
- */
843
- async addFilters(filters, additive = true) {
844
- if (!additive)
845
- this.filters = [];
846
- return this.filterStart(filters, additive);
847
- }
848
- // uses the first 'tr' of an active block as our yard stick
849
- set measureEle(el) {
850
- if (!el)
851
- return;
852
- this.measureHeight = el.getBoundingClientRect().height;
853
- this.unitHeight =
854
- el.querySelector('tr')?.getBoundingClientRect().height || this.unitHeight;
855
- }
856
- // Misc.
857
- get isReady() {
858
- return this._isReady;
859
- }
860
- set isReady(ready) {
861
- if (ready === this._isReady)
862
- return;
863
- this._isReady = ready;
864
- if (this.isReady)
865
- requestAnimationFrame(() => this.handleReady());
866
- }
867
- // Listeners
868
- scrollListener() {
869
- // see explanation in `setupBlockIO`
870
- const speed = detectScrollSpeed();
871
- this.scrollSpeed = speed < 0 ? speed * -1 : speed;
872
- }
873
- // Logic
874
- handleReady() {
875
- const hash = window.location.hash;
876
- if (hash.length > 1) {
877
- const idRow = document.querySelector(hash);
878
- if (idRow) {
879
- idRow.scrollIntoView();
880
- }
881
- }
882
- this.nanoTblReady.emit();
883
- }
884
- /**
885
- * Complete a sort. Reflects the order to ui.
886
- * @param order - column order
887
- * @param column - column config object
888
- */
889
- sortComplete(order, column) {
890
- this.columns = this.columns.map((c) => {
891
- if (c.prop === column)
892
- return { ...c, order };
893
- return { ...c, order: null };
894
- });
895
- this.nanoTblAfterSort.emit({ column: column, order });
896
- perMark('sort', true);
897
- }
898
- async searchStart() {
899
- this.loading = true;
900
- const sortEvent = this.nanoTblBeforeSearch.emit({ term: this.searchTerm });
901
- if (sortEvent.defaultPrevented)
902
- return;
903
- perMark('search');
904
- // doesn't make sense to leave user in place for a search
905
- this.scrollToTop();
906
- try {
907
- await storeSearch(this.host, this.searchTerm);
908
- this.nanoTblAfterSearch.emit({ term: this.searchTerm });
909
- perMark('search', true);
910
- }
911
- catch (e) {
912
- console.warn('search failed', e);
913
- }
914
- finally {
915
- this.loading = false;
916
- }
917
- }
918
- async filterStart(filters, additive = true) {
919
- if (filters) {
920
- if (additive) {
921
- this.filters = [
922
- ...this.filters.filter((f) => !filters.find((ff) => ff.prop === f.prop)),
923
- ...filters,
924
- ];
925
- }
926
- else {
927
- this.filters = filters;
928
- }
929
- }
930
- if (this.currentFilters === JSON.stringify(this.filters))
931
- return;
932
- this.loading = true;
933
- const sortEvent = this.nanoTblBeforeFilter.emit({ filters: this.filters });
934
- if (sortEvent.defaultPrevented)
935
- return;
936
- perMark('filter');
937
- this.currentFilters = JSON.stringify(this.filters);
938
- // doesn't make sense to leave user in place for a search
939
- this.scrollToTop();
940
- if (this.customFilterFn) {
941
- try {
942
- await this.customFilterFn(this.filters);
943
- this.filterComplete();
944
- }
945
- catch (e) {
946
- console.warn('custom filter failed', e);
947
- this.currentFilters = '';
948
- }
949
- return;
950
- }
951
- try {
952
- await storeFilter(this.host, this.filters);
953
- this.filterComplete();
954
- perMark('search', true);
955
- }
956
- catch (e) {
957
- console.warn('filter failed', e);
958
- }
959
- finally {
960
- this.loading = false;
961
- }
962
- }
963
- filterComplete() {
964
- this.columns = this.columns.map((c) => {
965
- const cFilter = this.filters.find((f) => f.prop === c.prop);
966
- if (cFilter)
967
- c.filter = cFilter.filter;
968
- return c;
969
- });
970
- this.nanoTblAfterFilter.emit({ filters: this.filters });
971
- perMark('filter', true);
972
- }
973
- /** Scrolls to the top immediately - used whilst sorting / filtering */
974
- scrollToTop(element) {
975
- const scrollBehaviour = this.scrollParent.style?.scrollBehavior;
976
- const scrollX = this.scrollParent.scrollLeft;
977
- this.scrollParent.style.scrollBehavior = 'auto';
978
- if (this.topAnchorEle && !isInViewport(this.topAnchorEle, 0.1)) {
979
- this.host.scrollIntoView();
980
- }
981
- if (element && !isInViewport(element, 1))
982
- setTimeout(() => element.scrollIntoView({ block: 'start' }), 500);
983
- if (scrollX)
984
- this.scrollParent.scrollLeft = scrollX;
985
- if (scrollBehaviour)
986
- this.scrollParent.style.scrollBehavior = scrollBehaviour;
987
- }
988
- setMeasureElement() {
989
- readTask(() => {
990
- this.measureEle = this.blockElements.find((b) => !b?.classList?.contains(`${CSSNAMESPACE}__inactive`));
991
- });
992
- }
993
- /**
994
- * Sets the initial height on tbody elements that are not active
995
- * These elements have no natural height - on account of all their rows being hidden
996
- * So we just estimate for now
997
- */
998
- setInitialBlockDimension() {
999
- if (!this.blockElements?.length)
1000
- return;
1001
- perMark('blockDims');
1002
- const finishResizing = new Promise((resolve) => {
1003
- readTask(() => {
1004
- this.setMeasureElement();
1005
- // find the first active block
1006
- if (!this.measureEle && !this.unitHeight)
1007
- resolve();
1008
- this.blockElements.forEach((el, i) => {
1009
- if (!el || !el.classList?.contains(`${CSSNAMESPACE}__inactive`)) {
1010
- if (i === this.blockElements.length - 1)
1011
- resolve();
1012
- return;
1013
- }
1014
- if (i === this.blockElements.length - 1)
1015
- resolve();
1016
- });
1017
- });
1018
- });
1019
- // we're all finished.
1020
- finishResizing.then(() => {
1021
- perMark('blockDims', true);
1022
- perMark('init', true);
1023
- requestAnimationFrame(() => {
1024
- this.isReady = true;
1025
- });
1026
- });
1027
- }
1028
- /** Apply initial columns settings */
1029
- async columnInit() {
1030
- this.filters = this.columns
1031
- .filter((c) => !!c.filter)
1032
- .map((c) => {
1033
- const { filter, prop } = c;
1034
- return { filter, prop };
1035
- });
1036
- if (this.searchTerm) {
1037
- await this.searchStart();
1038
- }
1039
- if (this.filters.length) {
1040
- await this.filterStart();
1041
- }
1042
- // apply sort
1043
- const col = this.columns.find((c) => !!c.order);
1044
- if (!!col) {
1045
- await this.sortStart(col.order, col.prop);
1046
- }
1047
- }
1048
- /** Split up all incoming rows into 'blocks' split amongst tbody elements.
1049
- * These can then be hidden / shown to improve performance.
1050
- */
1051
- setBlocks() {
1052
- perMark('setBlocks');
1053
- this.activeBlocks = [0, 1, 2];
1054
- this.ignoreIO = true;
1055
- let i = 1;
1056
- const l = this.store.data.state.rows.length;
1057
- let rows = [];
1058
- const blocks = [];
1059
- this.blockHeights = [];
1060
- // old skool loop for perf
1061
- for (i; i <= l; i++) {
1062
- rows.push(this.store.data.state.rows[i - 1]);
1063
- if (i % this.perBlock === 0) {
1064
- blocks.push({ rows, __uuid: cyrb53(rows.map((b) => b.__uuid).join()) });
1065
- rows = [];
1066
- }
1067
- }
1068
- if (rows.length)
1069
- blocks.push({ rows, __uuid: cyrb53(rows.map((b) => b.__uuid).join()) });
1070
- this.blocks = blocks;
1071
- perMark('setBlocks', true);
1072
- }
1073
- /**
1074
- * Returns a block render height.
1075
- * If it's currently active - let auto do it's thing
1076
- * If we've rendered it before - return that
1077
- * If all else fails, let's guess it
1078
- * @param blockIndex
1079
- * @returns a height string (incl px)
1080
- */
1081
- getBlockHeight(blockIndex) {
1082
- if (this.activeBlocks.includes(blockIndex))
1083
- return undefined;
1084
- if (this.blockHeights.length) {
1085
- const cachedBlockHeight = this.blockHeights.find((bh) => bh.blockIndex === blockIndex);
1086
- if (cachedBlockHeight)
1087
- return cachedBlockHeight.height + 'px';
1088
- }
1089
- const blockLength = this.blocks[blockIndex].rows.length;
1090
- if (blockLength === this.perBlock && this.measureHeight) {
1091
- return this.measureHeight + 'px';
1092
- }
1093
- return this.unitHeight ? this.unitHeight * blockLength + 'px' : undefined;
1094
- }
1095
- /** cache the height for all active blocks for later renders */
1096
- setBlockHeight() {
1097
- readTask(() => {
1098
- this.activeBlocks.forEach((blockIndex) => {
1099
- const el = this.blockElements[blockIndex];
1100
- if (!el)
1101
- return;
1102
- const height = el.getBoundingClientRect().height;
1103
- // cache height to our block heights array
1104
- // for subsequent renders
1105
- const fBhI = this.blockHeights.findIndex((bh) => bh.blockIndex === blockIndex);
1106
- if (fBhI > 0) {
1107
- this.blockHeights[fBhI] = { height, blockIndex };
1108
- }
1109
- else
1110
- this.blockHeights.push({ height, blockIndex });
1111
- });
1112
- });
1113
- }
1114
- /** Process slotted content */
1115
- processSlots() {
1116
- // see if we have slot content
1117
- if (!this.caption && !this.host.querySelector('[slot="caption"]')) {
1118
- console.error('For accessibility you must set a `title` prop or use the `caption` slot');
1119
- }
1120
- }
1121
- // Component lifecycle
1122
- async componentWillLoad() {
1123
- perMark('init');
1124
- this.store = await generateStore(this.host, this.columns, this.scrollParent);
1125
- await this.handleRowsChange();
1126
- this.processSlots();
1127
- this.setBlocks();
1128
- this.store.data.onChange('rows', () => this.setBlocks());
1129
- }
1130
- connectedCallback() {
1131
- readTask(() => (this.scrollParent = findScrollParent(this.host)));
1132
- }
1133
- componentDidLoad() {
1134
- this.setInitialBlockDimension();
1135
- }
1136
- componentShouldUpdate(_newVal, _oldVal, stateName) {
1137
- // stop double rendering - we use the store for rendering internally
1138
- // the public facing props are kept in-sync with the store
1139
- // but we don't want it to cause renders
1140
- if (['rows', 'columns'].includes(stateName))
1141
- return false;
1142
- }
1143
- componentWillRender() {
1144
- perMark('render');
1145
- }
1146
- componentDidRender() {
1147
- requestAnimationFrame(() => (this.ignoreIO = false));
1148
- this.setMeasureElement();
1149
- perMark('render', true);
1150
- }
1151
- render() {
1152
- this.blockElements = [];
1153
- 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" }, h("div", { class: `${CSSNAMESPACE}__wrap` }, h("nano-progress-bar", { indeterminate: true, class: {
1154
- [`${CSSNAMESPACE}__progress-bar`]: true,
1155
- [`${CSSNAMESPACE}__progress-bar--show`]: this._loading,
1156
- } }), 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: {
1157
- [`${CSSNAMESPACE}__caption`]: true,
1158
- [`${CSSNAMESPACE}__caption--hide`]: !this.showCaption,
1159
- }, 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) => [
1160
- h(TableColHead, { column: colModel, headRenderer: this.headRender, onColumnOrderClick: this.sortStart, onColumnPinned: this.handleColumnPinned }),
1161
- ]))), 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.blocks.map((block, blockIndex) => (h("tbody", { key: block.__uuid, id: `tbody-${this.renderId}-${blockIndex}`, ref: (tb) => {
1162
- this.blockElements.push(tb);
1163
- this.setupBlockIO(tb, blockIndex);
1164
- }, class: {
1165
- [`${CSSNAMESPACE}__inactive`]: !this.activeBlocks.includes(blockIndex),
1166
- [`${CSSNAMESPACE}__active`]: this.activeBlocks.includes(blockIndex),
1167
- } }, this.activeBlocks.includes(blockIndex) ? (block.rows.map((row, i) => {
1168
- const rowIndex = blockIndex > 0 ? blockIndex * this.perBlock + i : i;
1169
- return (h(TableRow, { rowRenderer: this.rowRender, row: row }, this.store.config.state.columns.map((_colModel, colIndex) => (h(TableCell, { rowIndex: rowIndex, colIndex: colIndex })))));
1170
- })) : (h("td", { colSpan: this.store.config.state.columns.length, style: {
1171
- height: this.getBlockHeight(blockIndex),
1172
- } }))))), this.showFooter && (h("tfoot", null, h(TableHeadFootRow, { rowRenderer: this.footRender, onColumnPinned: this.handleColumnPinned }, this.store.config.state.columns.map((colModel) => [
1173
- h(TableColHead, { column: colModel, headRenderer: this.footRender, onColumnPinned: this.handleColumnPinned, onColumnOrderClick: this.sortStart }),
1174
- ]))))), !!this.blocks.length && (h("nano-spinner", { type: "circle", class: {
1175
- [`${CSSNAMESPACE}__spinner`]: true,
1176
- [`${CSSNAMESPACE}__spinner--show`]: this._loading,
1177
- } }))))));
1178
- }
1179
- get host() { return this; }
1180
- static get watchers() { return {
1181
- "rows": ["handleRowsChange"],
1182
- "columns": ["handleColsChange"],
1183
- "searchTerm": ["handleSearchTermChange"]
1184
- }; }
1185
- static get style() { return tableCss; }
1186
- }, [4, "nano-table", {
1187
- "type": [1],
1188
- "caption": [1],
1189
- "showCaption": [4, "show-caption"],
1190
- "loading": [6148],
1191
- "placeholderSize": [2, "placeholder-size"],
1192
- "rows": [16],
1193
- "columns": [1040],
1194
- "headRender": [16],
1195
- "rowRender": [16],
1196
- "footRender": [16],
1197
- "showFooter": [4, "show-footer"],
1198
- "perBlock": [2, "per-block"],
1199
- "blocksLength": [2050, "blocks-length"],
1200
- "searchTerm": [1, "search-term"],
1201
- "customFilterFn": [16],
1202
- "customSortFn": [16],
1203
- "_loading": [32],
1204
- "blocks": [32],
1205
- "activeBlocks": [32],
1206
- "measureHeight": [32],
1207
- "resetSorting": [64],
1208
- "addSort": [64],
1209
- "resetFilters": [64],
1210
- "addFilters": [64]
1211
- }, [[9, "scroll", "scrollListener"]]]);
1212
- function defineCustomElement$1() {
1213
- if (typeof customElements === "undefined") {
1214
- return;
1215
- }
1216
- const components = ["nano-table", "nano-icon", "nano-progress-bar", "nano-resize-observe", "nano-skeleton", "nano-spinner"];
1217
- components.forEach(tagName => { switch (tagName) {
1218
- case "nano-table":
1219
- if (!customElements.get(tagName)) {
1220
- customElements.define(tagName, Table);
1221
- }
1222
- break;
1223
- case "nano-icon":
1224
- if (!customElements.get(tagName)) {
1225
- defineCustomElement$6();
1226
- }
1227
- break;
1228
- case "nano-progress-bar":
1229
- if (!customElements.get(tagName)) {
1230
- defineCustomElement$5();
1231
- }
1232
- break;
1233
- case "nano-resize-observe":
1234
- if (!customElements.get(tagName)) {
1235
- defineCustomElement$4();
1236
- }
1237
- break;
1238
- case "nano-skeleton":
1239
- if (!customElements.get(tagName)) {
1240
- defineCustomElement$3();
1241
- }
1242
- break;
1243
- case "nano-spinner":
1244
- if (!customElements.get(tagName)) {
1245
- defineCustomElement$2();
1246
- }
1247
- break;
1248
- } });
1249
- }
4
+ import { T as Table, d as defineCustomElement$1 } from './table.js';
1250
5
 
1251
6
  const NanoTable = Table;
1252
7
  const defineCustomElement = defineCustomElement$1;
1253
8
 
1254
- export { NanoTable, createWorker as c, defineCustomElement };
9
+ export { NanoTable, defineCustomElement };
1255
10
 
1256
11
  //# sourceMappingURL=nano-table.js.map