@alacris/ui 0.0.0 → 0.1.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 (94) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +148 -1
  3. package/package.json +75 -3
  4. package/src/components/base.js +46 -0
  5. package/src/components/ui-accordion-item.js +157 -0
  6. package/src/components/ui-accordion.js +58 -0
  7. package/src/components/ui-alert.js +168 -0
  8. package/src/components/ui-app-bar.js +119 -0
  9. package/src/components/ui-autocomplete.js +371 -0
  10. package/src/components/ui-avatar.js +104 -0
  11. package/src/components/ui-backdrop.js +66 -0
  12. package/src/components/ui-badge.js +95 -0
  13. package/src/components/ui-bottom-app-bar.js +78 -0
  14. package/src/components/ui-bottom-nav.js +83 -0
  15. package/src/components/ui-breadcrumbs.js +100 -0
  16. package/src/components/ui-button-group.js +49 -0
  17. package/src/components/ui-button.js +143 -0
  18. package/src/components/ui-card.js +86 -0
  19. package/src/components/ui-carousel-item.js +53 -0
  20. package/src/components/ui-carousel.js +301 -0
  21. package/src/components/ui-checkbox.js +165 -0
  22. package/src/components/ui-chip-set.js +90 -0
  23. package/src/components/ui-chip.js +226 -0
  24. package/src/components/ui-container.js +59 -0
  25. package/src/components/ui-date-picker.js +774 -0
  26. package/src/components/ui-dialog.js +179 -0
  27. package/src/components/ui-divider.js +58 -0
  28. package/src/components/ui-drawer.js +169 -0
  29. package/src/components/ui-fab-menu.js +122 -0
  30. package/src/components/ui-fab.js +117 -0
  31. package/src/components/ui-icon-button.js +112 -0
  32. package/src/components/ui-icon.js +64 -0
  33. package/src/components/ui-list-item.js +166 -0
  34. package/src/components/ui-list.js +38 -0
  35. package/src/components/ui-loading-indicator.js +74 -0
  36. package/src/components/ui-menu-item.js +117 -0
  37. package/src/components/ui-menu.js +192 -0
  38. package/src/components/ui-nav-item.js +138 -0
  39. package/src/components/ui-nav-rail.js +111 -0
  40. package/src/components/ui-option.js +85 -0
  41. package/src/components/ui-pagination.js +173 -0
  42. package/src/components/ui-progress.js +100 -0
  43. package/src/components/ui-radio-group.js +104 -0
  44. package/src/components/ui-radio.js +143 -0
  45. package/src/components/ui-rating.js +126 -0
  46. package/src/components/ui-search.js +316 -0
  47. package/src/components/ui-select.js +418 -0
  48. package/src/components/ui-sheet.js +205 -0
  49. package/src/components/ui-side-sheet.js +221 -0
  50. package/src/components/ui-skeleton.js +89 -0
  51. package/src/components/ui-slider.js +266 -0
  52. package/src/components/ui-snackbar.js +233 -0
  53. package/src/components/ui-spinner.js +101 -0
  54. package/src/components/ui-split-button.js +158 -0
  55. package/src/components/ui-stack.js +37 -0
  56. package/src/components/ui-step.js +91 -0
  57. package/src/components/ui-stepper.js +86 -0
  58. package/src/components/ui-surface.js +51 -0
  59. package/src/components/ui-switch.js +153 -0
  60. package/src/components/ui-tab-panel.js +54 -0
  61. package/src/components/ui-tab.js +116 -0
  62. package/src/components/ui-table-footer.js +141 -0
  63. package/src/components/ui-table-toolbar.js +219 -0
  64. package/src/components/ui-table.js +735 -0
  65. package/src/components/ui-tabs.js +178 -0
  66. package/src/components/ui-text-field.js +319 -0
  67. package/src/components/ui-text.js +36 -0
  68. package/src/components/ui-time-picker.js +753 -0
  69. package/src/components/ui-toggle-button.js +170 -0
  70. package/src/components/ui-toggle-group.js +111 -0
  71. package/src/components/ui-toolbar.js +64 -0
  72. package/src/components/ui-tooltip.js +148 -0
  73. package/src/index.js +121 -0
  74. package/src/motion/animate.js +145 -0
  75. package/src/motion/flip.js +46 -0
  76. package/src/motion/index.js +4 -0
  77. package/src/motion/presence.js +104 -0
  78. package/src/motion/ripple.js +108 -0
  79. package/src/theme/apply-theme.js +142 -0
  80. package/src/theme/create-theme.js +70 -0
  81. package/src/theme/index.js +5 -0
  82. package/src/tokens/color.js +237 -0
  83. package/src/tokens/index.js +14 -0
  84. package/src/tokens/sys.js +76 -0
  85. package/src/tokens/system.js +114 -0
  86. package/src/tokens/typography.js +189 -0
  87. package/src/util/focus.js +89 -0
  88. package/src/util/form.js +69 -0
  89. package/src/util/icons.js +161 -0
  90. package/src/util/keys.js +110 -0
  91. package/src/util/position.js +102 -0
  92. package/src/util/table.js +230 -0
  93. package/types/elements.d.ts +78 -0
  94. package/types/index.d.ts +168 -0
@@ -0,0 +1,230 @@
1
+ // Table data pipeline — pure functions used by <ui-table>.
2
+ //
3
+ // Order: normalize columns → filter → sort → group → paginate → aggregate.
4
+ // Each step is independently testable and reusable; the component only
5
+ // wires signals into this and renders the result.
6
+
7
+ export const titleCase = (k) => String(k)
8
+ .replace(/[_-]+/g, ' ')
9
+ .replace(/([a-z])([A-Z])/g, '$1 $2')
10
+ .replace(/\b\w/g, (c) => c.toUpperCase());
11
+
12
+ export const normalizeCol = (c) => {
13
+ if (typeof c === 'string') return { key: c, label: titleCase(c) };
14
+ return { label: c.label || titleCase(c.key), ...c };
15
+ };
16
+
17
+ export const inferColumns = (rows) => {
18
+ const first = (rows || []).find((r) => r && typeof r === 'object');
19
+ if (!first) return [];
20
+ return Object.keys(first)
21
+ .filter((k) => k !== 'id')
22
+ .map((key) => ({
23
+ key,
24
+ label: titleCase(key),
25
+ numeric: typeof first[key] === 'number',
26
+ sortable: true,
27
+ }));
28
+ };
29
+
30
+ export const visibleColumns = (columns, rows, hidden) => {
31
+ const raw = columns || [];
32
+ const list = (raw.length ? raw.map(normalizeCol) : inferColumns(rows))
33
+ .filter((c) => !c.hidden);
34
+ if (!hidden || !hidden.length) return list;
35
+ const skip = new Set(hidden);
36
+ const shown = list.filter((c) => !skip.has(c.key));
37
+ return shown.length ? shown : list.slice(0, 1);
38
+ };
39
+
40
+ export const compare = (a, b) => {
41
+ const aU = a == null || a === '';
42
+ const bU = b == null || b === '';
43
+ if (aU && bU) return 0;
44
+ if (aU) return 1;
45
+ if (bU) return -1;
46
+ if (typeof a === 'number' && typeof b === 'number') return a - b;
47
+ const an = +a;
48
+ const bn = +b;
49
+ if (a !== '' && b !== '' && !Number.isNaN(an) && !Number.isNaN(bn)) return an - bn;
50
+ return String(a).localeCompare(String(b), undefined, { numeric: true, sensitivity: 'base' });
51
+ };
52
+
53
+ export const sortValue = (row, col) => {
54
+ if (!col) return '';
55
+ if (typeof col.sortValue === 'function') return col.sortValue(row);
56
+ return row == null ? '' : row[col.key];
57
+ };
58
+
59
+ export const filterRows = (rows, cols, query) => {
60
+ if (!query) return rows || [];
61
+ const needle = String(query).toLowerCase();
62
+ return (rows || []).filter((row) => cols.some((c) => {
63
+ const v = sortValue(row, c);
64
+ return v != null && String(v).toLowerCase().includes(needle);
65
+ }));
66
+ };
67
+
68
+ export const sortRows = (rows, cols, sortBy, sortDir) => {
69
+ if (!sortBy) return rows || [];
70
+ const col = (cols || []).find((c) => c.key === sortBy);
71
+ const dir = sortDir === 'desc' ? -1 : 1;
72
+ return (rows || []).slice().sort((a, b) => dir * compare(sortValue(a, col), sortValue(b, col)));
73
+ };
74
+
75
+ export const paginate = (rows, page, pageSize) => {
76
+ const size = Math.max(0, Math.floor(pageSize) || 0);
77
+ if (!size) return rows || [];
78
+ const p = Math.max(1, Math.floor(page) || 1);
79
+ const start = (p - 1) * size;
80
+ return (rows || []).slice(start, start + size);
81
+ };
82
+
83
+ export const makeIdOf = (getRowId) => {
84
+ const anon = new WeakMap();
85
+ let n = 0;
86
+ return (row, i) => {
87
+ if (typeof getRowId === 'function') return getRowId(row, i);
88
+ if (row != null && row.id != null) return row.id;
89
+ if (row && typeof row === 'object') {
90
+ let id = anon.get(row);
91
+ if (id == null) { id = `row-${++n}`; anon.set(row, id); }
92
+ return id;
93
+ }
94
+ return i;
95
+ };
96
+ };
97
+
98
+ const asNumber = (v) => {
99
+ if (typeof v === 'number' && !Number.isNaN(v)) return v;
100
+ const n = +v;
101
+ return Number.isNaN(n) ? null : n;
102
+ };
103
+
104
+ export const aggregateValues = (rows, col, fn) => {
105
+ const kind = fn || col.aggregate;
106
+ if (!kind) return null;
107
+ const nums = [];
108
+ for (const row of rows || []) {
109
+ const n = asNumber(sortValue(row, col));
110
+ if (n != null) nums.push(n);
111
+ }
112
+ if (kind === 'count') return (rows || []).length;
113
+ if (!nums.length) return null;
114
+ if (kind === 'sum') return nums.reduce((a, b) => a + b, 0);
115
+ if (kind === 'avg') return nums.reduce((a, b) => a + b, 0) / nums.length;
116
+ if (kind === 'min') return Math.min(...nums);
117
+ if (kind === 'max') return Math.max(...nums);
118
+ return null;
119
+ };
120
+
121
+ export const aggregateRow = (rows, cols) => {
122
+ const out = {};
123
+ for (const col of cols || []) {
124
+ if (!col.aggregate) continue;
125
+ out[col.key] = aggregateValues(rows, col, col.aggregate);
126
+ }
127
+ return out;
128
+ };
129
+
130
+ export const formatAgg = (v) => {
131
+ if (v == null) return '';
132
+ if (typeof v !== 'number') return String(v);
133
+ if (Number.isInteger(v)) return v.toLocaleString();
134
+ return v.toLocaleString(undefined, { maximumFractionDigits: 2 });
135
+ };
136
+
137
+ /** Flatten rows into group headers + member rows. `expanded` is a Set of group keys; omit to expand all. */
138
+ export const groupRows = (rows, groupBy, { expanded, idOf, cols } = {}) => {
139
+ const list = rows || [];
140
+ const id = idOf || ((row, i) => (row && row.id != null ? row.id : i));
141
+ if (!groupBy) {
142
+ return list.map((row, i) => ({ type: 'row', id: id(row, i), row }));
143
+ }
144
+ const buckets = new Map();
145
+ for (const row of list) {
146
+ const raw = row == null ? '' : row[groupBy];
147
+ const key = raw == null ? '' : String(raw);
148
+ let bucket = buckets.get(key);
149
+ if (!bucket) { bucket = []; buckets.set(key, bucket); }
150
+ bucket.push(row);
151
+ }
152
+ const out = [];
153
+ for (const [key, members] of buckets) {
154
+ const open = !expanded || expanded.has(key);
155
+ out.push({
156
+ type: 'group',
157
+ id: `group:${key}`,
158
+ key,
159
+ label: key,
160
+ count: members.length,
161
+ rows: members,
162
+ expanded: open,
163
+ aggregates: aggregateRow(members, cols),
164
+ });
165
+ if (open) {
166
+ for (let i = 0; i < members.length; i++) {
167
+ out.push({ type: 'row', id: id(members[i], i), row: members[i], group: key, depth: 1 });
168
+ }
169
+ }
170
+ }
171
+ return out;
172
+ };
173
+
174
+ export const toCsv = (rows, cols) => {
175
+ const esc = (v) => {
176
+ const s = v == null ? '' : String(v);
177
+ return /[",\n\r]/.test(s) ? `"${s.replace(/"/g, '""')}"` : s;
178
+ };
179
+ const header = (cols || []).map((c) => esc(c.label)).join(',');
180
+ const body = (rows || []).map((row) =>
181
+ (cols || []).map((c) => esc(sortValue(row, c))).join(',')).join('\n');
182
+ return body ? `${header}\n${body}` : header;
183
+ };
184
+
185
+ export const downloadText = (filename, text, type = 'text/csv;charset=utf-8') => {
186
+ const blob = new Blob([text], { type });
187
+ const url = URL.createObjectURL(blob);
188
+ const a = document.createElement('a');
189
+ a.href = url;
190
+ a.download = filename;
191
+ a.rel = 'noopener';
192
+ document.body.append(a);
193
+ a.click();
194
+ a.remove();
195
+ URL.revokeObjectURL(url);
196
+ };
197
+
198
+ /**
199
+ * Run the full client-side pipeline. Server modes skip filter/sort/page.
200
+ */
201
+ export const processTable = ({
202
+ rows = [],
203
+ columns = [],
204
+ hiddenColumns = [],
205
+ filter = '',
206
+ sortBy = '',
207
+ sortDir = 'asc',
208
+ sortMode = 'client',
209
+ groupBy = '',
210
+ expanded,
211
+ page = 1,
212
+ pageSize = 0,
213
+ paginationMode = 'client',
214
+ rowCount = 0,
215
+ idOf,
216
+ } = {}) => {
217
+ const cols = visibleColumns(columns, rows, hiddenColumns);
218
+ let list = rows || [];
219
+ if (sortMode !== 'server') {
220
+ list = filterRows(list, cols, filter);
221
+ list = sortRows(list, cols, sortBy, sortDir);
222
+ }
223
+ const filteredCount = (Math.floor(rowCount) || 0) > 0 ? Math.floor(rowCount) : list.length;
224
+ const items = groupRows(list, groupBy, { expanded, idOf, cols });
225
+ const visible = paginationMode === 'server' || !pageSize
226
+ ? items
227
+ : paginate(items, page, pageSize);
228
+ const totals = aggregateRow(list, cols);
229
+ return { cols, list, filteredCount, items, visible, totals };
230
+ };
@@ -0,0 +1,78 @@
1
+ // Tag map so querySelector('ui-button') and similar type-check.
2
+ // Component modules register the custom element as a side effect; they have
3
+ // no runtime export besides that (and showSnackbar on ui-snackbar).
4
+
5
+ export {};
6
+
7
+ declare global {
8
+ interface HTMLElementTagNameMap {
9
+ 'ui-accordion': HTMLElement;
10
+ 'ui-accordion-item': HTMLElement;
11
+ 'ui-alert': HTMLElement;
12
+ 'ui-app-bar': HTMLElement;
13
+ 'ui-autocomplete': HTMLElement;
14
+ 'ui-avatar': HTMLElement;
15
+ 'ui-backdrop': HTMLElement;
16
+ 'ui-badge': HTMLElement;
17
+ 'ui-bottom-app-bar': HTMLElement;
18
+ 'ui-bottom-nav': HTMLElement;
19
+ 'ui-breadcrumbs': HTMLElement;
20
+ 'ui-button': HTMLElement;
21
+ 'ui-button-group': HTMLElement;
22
+ 'ui-card': HTMLElement;
23
+ 'ui-carousel': HTMLElement;
24
+ 'ui-carousel-item': HTMLElement;
25
+ 'ui-checkbox': HTMLElement;
26
+ 'ui-chip': HTMLElement;
27
+ 'ui-chip-set': HTMLElement;
28
+ 'ui-container': HTMLElement;
29
+ 'ui-date-picker': HTMLElement;
30
+ 'ui-dialog': HTMLElement;
31
+ 'ui-divider': HTMLElement;
32
+ 'ui-drawer': HTMLElement;
33
+ 'ui-fab': HTMLElement;
34
+ 'ui-fab-menu': HTMLElement;
35
+ 'ui-icon': HTMLElement;
36
+ 'ui-icon-button': HTMLElement;
37
+ 'ui-list': HTMLElement;
38
+ 'ui-list-item': HTMLElement;
39
+ 'ui-loading-indicator': HTMLElement;
40
+ 'ui-menu': HTMLElement;
41
+ 'ui-menu-item': HTMLElement;
42
+ 'ui-nav-item': HTMLElement;
43
+ 'ui-nav-rail': HTMLElement;
44
+ 'ui-option': HTMLElement;
45
+ 'ui-pagination': HTMLElement;
46
+ 'ui-progress': HTMLElement;
47
+ 'ui-radio': HTMLElement;
48
+ 'ui-radio-group': HTMLElement;
49
+ 'ui-rating': HTMLElement;
50
+ 'ui-search': HTMLElement;
51
+ 'ui-select': HTMLElement;
52
+ 'ui-sheet': HTMLElement;
53
+ 'ui-side-sheet': HTMLElement;
54
+ 'ui-skeleton': HTMLElement;
55
+ 'ui-slider': HTMLElement;
56
+ 'ui-snackbar': HTMLElement;
57
+ 'ui-spinner': HTMLElement;
58
+ 'ui-split-button': HTMLElement;
59
+ 'ui-stack': HTMLElement;
60
+ 'ui-step': HTMLElement;
61
+ 'ui-stepper': HTMLElement;
62
+ 'ui-surface': HTMLElement;
63
+ 'ui-switch': HTMLElement;
64
+ 'ui-tab': HTMLElement;
65
+ 'ui-tab-panel': HTMLElement;
66
+ 'ui-table': HTMLElement;
67
+ 'ui-table-footer': HTMLElement;
68
+ 'ui-table-toolbar': HTMLElement;
69
+ 'ui-tabs': HTMLElement;
70
+ 'ui-text': HTMLElement;
71
+ 'ui-text-field': HTMLElement;
72
+ 'ui-time-picker': HTMLElement;
73
+ 'ui-toggle-button': HTMLElement;
74
+ 'ui-toggle-group': HTMLElement;
75
+ 'ui-toolbar': HTMLElement;
76
+ 'ui-tooltip': HTMLElement;
77
+ }
78
+ }
@@ -0,0 +1,168 @@
1
+ // Hand-written types for @alacris/ui. They track starter/src/index.js and the
2
+ // theme / motion / token / util entry points — not generated.
3
+ import type { Signal, Computed, Template } from '@alacris/core';
4
+ import './elements.js';
5
+
6
+ export type Scheme = 'light' | 'dark';
7
+ export type SchemePreference = Scheme | 'auto';
8
+
9
+ export interface ThemeConfig {
10
+ seed?: string;
11
+ colors?: Record<string, string>;
12
+ typography?: string | {
13
+ preset?: string;
14
+ family?: string;
15
+ brand?: string;
16
+ plain?: string;
17
+ code?: string;
18
+ scale?: number;
19
+ load?: boolean;
20
+ };
21
+ shape?: { radius?: number };
22
+ motion?: { scale?: number };
23
+ density?: number;
24
+ overrides?: {
25
+ common?: Record<string, string>;
26
+ light?: Record<string, string>;
27
+ dark?: Record<string, string>;
28
+ };
29
+ loadFonts?: boolean;
30
+ }
31
+
32
+ export interface Theme {
33
+ config: ThemeConfig;
34
+ palettes: unknown;
35
+ common: Record<string, string>;
36
+ schemes: { light: Record<string, string>; dark: Record<string, string> };
37
+ fonts: { href: string | null; preset: string };
38
+ }
39
+
40
+ export function createTheme(config?: ThemeConfig): Theme;
41
+ export function applyTheme(themeOrConfig?: Theme | ThemeConfig): Theme;
42
+ export function themeCss(theme: Theme): string;
43
+ export const activeTheme: Signal<Theme | null>;
44
+ export function loadThemeFonts(theme: Pick<Theme, 'fonts'> | { fonts: { href: null } }, doc?: Document): void;
45
+ export const schemePreference: Signal<SchemePreference>;
46
+ export const scheme: Computed<Scheme>;
47
+ export function setScheme(pref: SchemePreference): void;
48
+ export function toggleScheme(): void;
49
+
50
+ export const sys: {
51
+ color: Record<string, string>;
52
+ radius: Record<string, string>;
53
+ elevation: Record<number, string>;
54
+ duration: Record<string, string>;
55
+ easing: Record<string, string>;
56
+ type: Record<string, string>;
57
+ tracking: Record<string, string>;
58
+ font: { brand: string; plain: string; code: string };
59
+ space: (n: number) => string;
60
+ state: Record<string, string>;
61
+ focus: Record<string, string>;
62
+ z: Record<string, string>;
63
+ density: string;
64
+ };
65
+ export function typeRule(role: string): string;
66
+
67
+ export const FONT_PRESETS: Record<string, { brand: string; plain: string; href: string | null }>;
68
+ export const FONT_STACKS: Record<string, string>;
69
+ export const DEFAULT_FONT_PRESET: string;
70
+ export const TYPE_ROLES: string[];
71
+ export function resolveTypography(config?: ThemeConfig['typography']): {
72
+ brand: string;
73
+ plain: string;
74
+ code: string;
75
+ href: string | null;
76
+ preset: string;
77
+ };
78
+ export function googleFontsHref(names: string | string[]): string;
79
+
80
+ export type Keyframes = Keyframe[] | PropertyIndexedKeyframes | ((el: Element) => Keyframe[]);
81
+ export interface AnimateOptions extends KeyframeAnimationOptions {
82
+ duration?: number | string;
83
+ easing?: string;
84
+ }
85
+
86
+ export function animate(el: Element, keyframes: Keyframes, opts?: AnimateOptions): Pick<Animation, 'finished' | 'cancel' | 'finish' | 'play' | 'pause'>;
87
+ export function settled(anim: { finished: Promise<unknown> }): Promise<void>;
88
+ export function duration(key: number | string): number;
89
+ export function easing(key: string): string;
90
+ export const prefersReducedMotion: () => boolean;
91
+ export const fx: Record<string, Keyframes>;
92
+
93
+ export function presence(
94
+ when: Signal<unknown> | (() => unknown),
95
+ view: () => Template | Node,
96
+ opts?: { enter?: Keyframes | false; exit?: Keyframes | false }
97
+ ): Node;
98
+
99
+ export function withFlip<T>(
100
+ container: Element,
101
+ mutate: () => T,
102
+ opts?: { duration?: number | string; easing?: string; stagger?: number }
103
+ ): T;
104
+
105
+ export function ripple(
106
+ el: Element,
107
+ opts?: { disabled?: boolean | Signal<boolean> | (() => boolean); centered?: boolean }
108
+ ): Element;
109
+
110
+ export function position(
111
+ panel: HTMLElement,
112
+ anchor: Element,
113
+ opts?: {
114
+ placement?: string;
115
+ offset?: number;
116
+ flip?: boolean;
117
+ matchWidth?: boolean;
118
+ padding?: number;
119
+ }
120
+ ): { placement: string };
121
+ export function autoUpdate(panel: HTMLElement, anchor: Element, opts?: object): () => void;
122
+
123
+ export function focusTrap(host: Element, opts?: { initial?: Element }): () => void;
124
+ export function focusables(host: Element): Element[];
125
+ export function scrollLock(lock?: boolean): void;
126
+
127
+ export function rovingTabindex(
128
+ container: Element,
129
+ opts?: {
130
+ selector?: string;
131
+ items?: () => Element[];
132
+ listenOn?: EventTarget;
133
+ orientation?: 'horizontal' | 'vertical' | 'both';
134
+ wrap?: boolean;
135
+ skip?: (el: Element) => boolean;
136
+ onMove?: (el: Element, index: number) => void;
137
+ }
138
+ ): { focus: (el: Element | number) => void; activate: (el: Element | number) => void; refresh: () => void; destroy: () => void };
139
+
140
+ export function formBind(
141
+ host: { internals?: ElementInternals; onFormReset?: () => void },
142
+ fields: {
143
+ name?: Signal<string>;
144
+ value?: Signal<unknown>;
145
+ checked?: Signal<boolean>;
146
+ disabled?: Signal<boolean>;
147
+ }
148
+ ): void;
149
+
150
+ export function registerIcons(icons: Record<string, string>): void;
151
+ export function iconPath(name: string): string | undefined;
152
+ export function iconNames(): string[];
153
+
154
+ export function processTable(opts: Record<string, unknown>): Record<string, unknown>;
155
+ export function filterRows(rows: object[], cols: object[], query: string): object[];
156
+ export function sortRows(rows: object[], cols: object[], sortBy: string, sortDir: string): object[];
157
+ export function groupRows(rows: object[], groupBy: string, opts?: object): object[];
158
+ export function paginate(rows: object[], page: number, pageSize: number): { rows: object[]; page: number; pages: number };
159
+ export function aggregateRow(rows: object[], cols: object[]): object;
160
+ export function toCsv(rows: object[], cols: object[]): string;
161
+ export function downloadText(filename: string, text: string, type?: string): void;
162
+ export function inferColumns(rows: object[]): object[];
163
+ export function visibleColumns(columns: object[] | undefined, rows: object[], hidden?: string[]): object[];
164
+
165
+ export function showSnackbar(
166
+ message: string,
167
+ opts?: { action?: string; duration?: number; closeButton?: boolean }
168
+ ): { close: () => void; closed: Promise<void> };