@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.
Files changed (101) hide show
  1. package/CHANGELOG.md +12 -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-83e46f68.js} +486 -202
  9. package/dist/cjs/nano-table-83e46f68.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-525ec230.js} +3 -3
  12. package/dist/cjs/table.worker-525ec230.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-e2405350.js} +487 -203
  56. package/dist/esm/nano-table-e2405350.js.map +1 -0
  57. package/dist/esm/nano-table.entry.js +1 -1
  58. package/dist/esm/{table.worker-65438fa0.js → table.worker-739c193f.js} +3 -3
  59. package/dist/esm/table.worker-739c193f.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-28fdfa6b.js +5 -0
  65. package/dist/nano-components/p-28fdfa6b.js.map +1 -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-a71989f3.js +5 -0
  69. package/dist/nano-components/{p-14218d23.entry.js.map → p-a71989f3.js.map} +0 -0
  70. package/dist/nano-components/{p-14218d23.entry.js → p-e4a28360.entry.js} +2 -2
  71. package/dist/nano-components/{p-fe1f8360.js.map → p-e4a28360.entry.js.map} +0 -0
  72. package/dist/nano-components/p-e9fb087e.js +4 -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 +2 -2
  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
@@ -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