@siyahi/ui 0.0.0-stage → 0.1.0-next.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +174 -2
- package/THIRD_PARTY_NOTICES.md +51 -0
- package/dist/components/registry/dashboard-01/section-cards.d.ts +17 -0
- package/dist/components/registry/dashboard-01/section-cards.js +39 -0
- package/dist/components/registry/dashboardcn/bar-list.d.ts +24 -0
- package/dist/components/registry/dashboardcn/bar-list.js +71 -0
- package/dist/components/registry/heatmap/calendar-heatmap.d.ts +134 -0
- package/dist/components/registry/heatmap/calendar-heatmap.js +497 -0
- package/dist/components/registry/heatmap/status-heatmap.d.ts +120 -0
- package/dist/components/registry/heatmap/status-heatmap.js +330 -0
- package/dist/components/registry/heatmap/weekday-heatmap.d.ts +145 -0
- package/dist/components/registry/heatmap/weekday-heatmap.js +489 -0
- package/dist/components/registry/niko-table/components/data-table-column-actions.d.ts +26 -0
- package/dist/components/registry/niko-table/components/data-table-column-actions.js +32 -0
- package/dist/components/registry/niko-table/components/data-table-column-filter.d.ts +13 -0
- package/dist/components/registry/niko-table/components/data-table-column-filter.js +17 -0
- package/dist/components/registry/niko-table/components/data-table-column-header.d.ts +30 -0
- package/dist/components/registry/niko-table/components/data-table-column-header.js +35 -0
- package/dist/components/registry/niko-table/components/data-table-column-sort.d.ts +21 -0
- package/dist/components/registry/niko-table/components/data-table-column-sort.js +36 -0
- package/dist/components/registry/niko-table/components/data-table-column-title.d.ts +12 -0
- package/dist/components/registry/niko-table/components/data-table-column-title.js +18 -0
- package/dist/components/registry/niko-table/components/data-table-empty-state.d.ts +108 -0
- package/dist/components/registry/niko-table/components/data-table-empty-state.js +148 -0
- package/dist/components/registry/niko-table/components/data-table-faceted-filter.d.ts +46 -0
- package/dist/components/registry/niko-table/components/data-table-faceted-filter.js +133 -0
- package/dist/components/registry/niko-table/components/data-table-grouped-rows.d.ts +111 -0
- package/dist/components/registry/niko-table/components/data-table-grouped-rows.js +77 -0
- package/dist/components/registry/niko-table/components/data-table-row-context-menu-slot.d.ts +65 -0
- package/dist/components/registry/niko-table/components/data-table-row-context-menu-slot.js +65 -0
- package/dist/components/registry/niko-table/components/data-table-row-context-menu.d.ts +50 -0
- package/dist/components/registry/niko-table/components/data-table-row-context-menu.js +42 -0
- package/dist/components/registry/niko-table/components/data-table-row-menu.d.ts +51 -0
- package/dist/components/registry/niko-table/components/data-table-row-menu.js +65 -0
- package/dist/components/registry/niko-table/components/data-table-toolbar-section.d.ts +7 -0
- package/dist/components/registry/niko-table/components/data-table-toolbar-section.js +63 -0
- package/dist/components/registry/niko-table/config/data-table.d.ts +156 -0
- package/dist/components/registry/niko-table/config/data-table.js +279 -0
- package/dist/components/registry/niko-table/config/feature-detection.d.ts +39 -0
- package/dist/components/registry/niko-table/config/feature-detection.js +174 -0
- package/dist/components/registry/niko-table/core/data-table-context.d.ts +157 -0
- package/dist/components/registry/niko-table/core/data-table-context.js +326 -0
- package/dist/components/registry/niko-table/core/data-table-error-boundary.d.ts +85 -0
- package/dist/components/registry/niko-table/core/data-table-error-boundary.js +116 -0
- package/dist/components/registry/niko-table/core/data-table-grouped-row.d.ts +49 -0
- package/dist/components/registry/niko-table/core/data-table-grouped-row.js +112 -0
- package/dist/components/registry/niko-table/core/data-table-root.d.ts +62 -0
- package/dist/components/registry/niko-table/core/data-table-root.js +406 -0
- package/dist/components/registry/niko-table/core/data-table-structure.d.ts +153 -0
- package/dist/components/registry/niko-table/core/data-table-structure.js +370 -0
- package/dist/components/registry/niko-table/core/data-table.d.ts +30 -0
- package/dist/components/registry/niko-table/core/data-table.js +126 -0
- package/dist/components/registry/niko-table/filters/table-column-actions.d.ts +49 -0
- package/dist/components/registry/niko-table/filters/table-column-actions.js +48 -0
- package/dist/components/registry/niko-table/filters/table-column-sort.d.ts +51 -0
- package/dist/components/registry/niko-table/filters/table-column-sort.js +184 -0
- package/dist/components/registry/niko-table/filters/table-column-title.d.ts +17 -0
- package/dist/components/registry/niko-table/filters/table-column-title.js +20 -0
- package/dist/components/registry/niko-table/filters/table-faceted-filter.d.ts +40 -0
- package/dist/components/registry/niko-table/filters/table-faceted-filter.js +210 -0
- package/dist/components/registry/niko-table/hooks/use-derived-column-title.d.ts +29 -0
- package/dist/components/registry/niko-table/hooks/use-derived-column-title.js +40 -0
- package/dist/components/registry/niko-table/hooks/use-generated-options.d.ts +49 -0
- package/dist/components/registry/niko-table/hooks/use-generated-options.js +144 -0
- package/dist/components/registry/niko-table/lib/build-faceted-options.d.ts +59 -0
- package/dist/components/registry/niko-table/lib/build-faceted-options.js +82 -0
- package/dist/components/registry/niko-table/lib/column-resize-handle.d.ts +33 -0
- package/dist/components/registry/niko-table/lib/column-resize-handle.js +175 -0
- package/dist/components/registry/niko-table/lib/constants.d.ts +128 -0
- package/dist/components/registry/niko-table/lib/constants.js +123 -0
- package/dist/components/registry/niko-table/lib/create-scroll-handler.d.ts +35 -0
- package/dist/components/registry/niko-table/lib/create-scroll-handler.js +63 -0
- package/dist/components/registry/niko-table/lib/data-table-features.d.ts +218 -0
- package/dist/components/registry/niko-table/lib/data-table-features.js +127 -0
- package/dist/components/registry/niko-table/lib/filter-functions.d.ts +47 -0
- package/dist/components/registry/niko-table/lib/filter-functions.js +309 -0
- package/dist/components/registry/niko-table/lib/filter-rows.d.ts +14 -0
- package/dist/components/registry/niko-table/lib/filter-rows.js +28 -0
- package/dist/components/registry/niko-table/lib/flex-columns.d.ts +48 -0
- package/dist/components/registry/niko-table/lib/flex-columns.js +65 -0
- package/dist/components/registry/niko-table/lib/format.d.ts +33 -0
- package/dist/components/registry/niko-table/lib/format.js +99 -0
- package/dist/components/registry/niko-table/lib/render-cell-content.d.ts +17 -0
- package/dist/components/registry/niko-table/lib/render-cell-content.js +65 -0
- package/dist/components/registry/niko-table/lib/row-click.d.ts +67 -0
- package/dist/components/registry/niko-table/lib/row-click.js +28 -0
- package/dist/components/registry/niko-table/lib/styles.d.ts +6 -0
- package/dist/components/registry/niko-table/lib/styles.js +24 -0
- package/dist/components/registry/niko-table/lib/use-header-min-widths.d.ts +17 -0
- package/dist/components/registry/niko-table/lib/use-header-min-widths.js +82 -0
- package/dist/components/registry/niko-table/types/index.d.ts +141 -0
- package/dist/components/registry/niko-table/types/index.js +2 -0
- package/dist/components/registry/niko-table/ui/table.d.ts +13 -0
- package/dist/components/registry/niko-table/ui/table.js +74 -0
- package/dist/components/registry/sidebar-07/app-sidebar.d.ts +18 -0
- package/dist/components/registry/sidebar-07/app-sidebar.js +26 -0
- package/dist/components/registry/sidebar-07/nav-main.d.ts +15 -0
- package/dist/components/registry/sidebar-07/nav-main.js +25 -0
- package/dist/components/registry/sidebar-07/nav-projects.d.ts +9 -0
- package/dist/components/registry/sidebar-07/nav-projects.js +43 -0
- package/dist/components/registry/sidebar-07/nav-user.d.ts +9 -0
- package/dist/components/registry/sidebar-07/nav-user.js +70 -0
- package/dist/components/registry/sidebar-07/team-switcher.d.ts +10 -0
- package/dist/components/registry/sidebar-07/team-switcher.js +70 -0
- package/dist/components/siyahi/delta.d.ts +10 -0
- package/dist/components/siyahi/delta.js +27 -0
- package/dist/components/siyahi/local-time.d.ts +7 -0
- package/dist/components/siyahi/local-time.js +22 -0
- package/dist/components/siyahi/price-chart.d.ts +12 -0
- package/dist/components/siyahi/price-chart.js +80 -0
- package/dist/components/siyahi/siyahi-provider.d.ts +12 -0
- package/dist/components/siyahi/siyahi-provider.js +31 -0
- package/dist/components/siyahi/treemap.d.ts +14 -0
- package/dist/components/siyahi/treemap.js +58 -0
- package/dist/components/ui/alert.d.ts +12 -0
- package/dist/components/ui/alert.js +43 -0
- package/dist/components/ui/avatar.d.ts +13 -0
- package/dist/components/ui/avatar.js +50 -0
- package/dist/components/ui/badge.d.ts +9 -0
- package/dist/components/ui/badge.js +29 -0
- package/dist/components/ui/breadcrumb.d.ts +12 -0
- package/dist/components/ui/breadcrumb.js +72 -0
- package/dist/components/ui/button.d.ts +10 -0
- package/dist/components/ui/button.js +44 -0
- package/dist/components/ui/calendar.d.ts +12 -0
- package/dist/components/ui/calendar.js +107 -0
- package/dist/components/ui/card.d.ts +13 -0
- package/dist/components/ui/card.js +56 -0
- package/dist/components/ui/chart.d.ts +46 -0
- package/dist/components/ui/chart.js +149 -0
- package/dist/components/ui/collapsible.d.ts +7 -0
- package/dist/components/ui/collapsible.js +24 -0
- package/dist/components/ui/command.d.ts +21 -0
- package/dist/components/ui/command.js +88 -0
- package/dist/components/ui/context-menu.d.ts +31 -0
- package/dist/components/ui/context-menu.js +132 -0
- package/dist/components/ui/dialog.d.ts +19 -0
- package/dist/components/ui/dialog.js +91 -0
- package/dist/components/ui/dropdown-menu.d.ts +31 -0
- package/dist/components/ui/dropdown-menu.js +136 -0
- package/dist/components/ui/input-group.d.ts +20 -0
- package/dist/components/ui/input-group.js +79 -0
- package/dist/components/ui/input.d.ts +5 -0
- package/dist/components/ui/input.js +15 -0
- package/dist/components/ui/popover.d.ts +11 -0
- package/dist/components/ui/popover.js +55 -0
- package/dist/components/ui/progress.d.ts +9 -0
- package/dist/components/ui/progress.js +43 -0
- package/dist/components/ui/scroll-area.d.ts +7 -0
- package/dist/components/ui/scroll-area.js +36 -0
- package/dist/components/ui/select.d.ts +17 -0
- package/dist/components/ui/select.js +97 -0
- package/dist/components/ui/separator.d.ts +5 -0
- package/dist/components/ui/separator.js +15 -0
- package/dist/components/ui/sheet.d.ts +16 -0
- package/dist/components/ui/sheet.js +89 -0
- package/dist/components/ui/sidebar.d.ts +65 -0
- package/dist/components/ui/sidebar.js +385 -0
- package/dist/components/ui/skeleton.d.ts +4 -0
- package/dist/components/ui/skeleton.js +12 -0
- package/dist/components/ui/slider.d.ts +5 -0
- package/dist/components/ui/slider.js +33 -0
- package/dist/components/ui/table.d.ts +12 -0
- package/dist/components/ui/table.js +66 -0
- package/dist/components/ui/tabs.d.ts +12 -0
- package/dist/components/ui/tabs.js +45 -0
- package/dist/components/ui/textarea.d.ts +5 -0
- package/dist/components/ui/textarea.js +13 -0
- package/dist/components/ui/toggle-group.d.ts +13 -0
- package/dist/components/ui/toggle-group.js +52 -0
- package/dist/components/ui/toggle.d.ts +10 -0
- package/dist/components/ui/toggle.js +36 -0
- package/dist/components/ui/tooltip.d.ts +8 -0
- package/dist/components/ui/tooltip.js +41 -0
- package/dist/hooks/use-mobile.d.ts +3 -0
- package/dist/hooks/use-mobile.js +18 -0
- package/dist/index.d.ts +73 -0
- package/dist/index.js +71 -0
- package/dist/lib/format.d.ts +24 -0
- package/dist/lib/format.js +58 -0
- package/dist/lib/locale.d.ts +12 -0
- package/dist/lib/locale.js +8 -0
- package/dist/lib/recharts.d.ts +2 -0
- package/dist/lib/recharts.js +1 -0
- package/dist/lib/utils.d.ts +2 -0
- package/dist/lib/utils.js +1 -0
- package/package.json +78 -3
- package/registry.lock.json +413 -0
- package/report.css +129 -0
- package/styles/shadcn.css +643 -0
- package/styles/siyahi.css +189 -0
|
@@ -0,0 +1,309 @@
|
|
|
1
|
+
import { FILTER_OPERATORS, FILTER_VARIANTS, JOIN_OPERATORS } from "./constants.js";
|
|
2
|
+
//#region src/components/registry/niko-table/lib/filter-functions.ts
|
|
3
|
+
const regexCache = /* @__PURE__ */ new Map();
|
|
4
|
+
const MAX_REGEX_CACHE_SIZE = 100;
|
|
5
|
+
function getOrCreateRegex(pattern, flags) {
|
|
6
|
+
const key = `${pattern}:${flags}`;
|
|
7
|
+
if (regexCache.has(key)) {
|
|
8
|
+
const cachedRegex = regexCache.get(key);
|
|
9
|
+
if (cachedRegex !== void 0) return cachedRegex;
|
|
10
|
+
}
|
|
11
|
+
if (regexCache.size >= MAX_REGEX_CACHE_SIZE) {
|
|
12
|
+
const firstKey = regexCache.keys().next().value;
|
|
13
|
+
if (firstKey !== void 0) regexCache.delete(firstKey);
|
|
14
|
+
}
|
|
15
|
+
try {
|
|
16
|
+
const regex = new RegExp(pattern, flags);
|
|
17
|
+
regexCache.set(key, regex);
|
|
18
|
+
return regex;
|
|
19
|
+
} catch {
|
|
20
|
+
const fallbackRegex = /(?!)/;
|
|
21
|
+
regexCache.set(key, fallbackRegex);
|
|
22
|
+
return fallbackRegex;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Custom filter function that handles our extended filter operators
|
|
27
|
+
*/
|
|
28
|
+
const extendedFilter = (row, columnId, filterValue, _addMeta) => {
|
|
29
|
+
if (!filterValue) return true;
|
|
30
|
+
if (typeof filterValue === "object" && filterValue.operator && filterValue.value !== void 0) {
|
|
31
|
+
const filter = filterValue;
|
|
32
|
+
return applyFilterOperator(row.getValue(columnId), filter.operator, filter.value);
|
|
33
|
+
}
|
|
34
|
+
if (Array.isArray(filterValue)) {
|
|
35
|
+
const cellValue = row.getValue(columnId);
|
|
36
|
+
if (cellValue == null) return false;
|
|
37
|
+
if (filterValue.length === 2 && typeof filterValue[0] === "number" && typeof filterValue[1] === "number") {
|
|
38
|
+
const [min, max] = filterValue;
|
|
39
|
+
const value = Number(cellValue);
|
|
40
|
+
if (isNaN(value)) return false;
|
|
41
|
+
return value >= min && value <= max;
|
|
42
|
+
}
|
|
43
|
+
if (typeof cellValue === "string") {
|
|
44
|
+
const cellLower = cellValue.toLowerCase();
|
|
45
|
+
return filterValue.some((val) => typeof val === "string" ? val.toLowerCase() === cellLower : String(val) === cellValue);
|
|
46
|
+
}
|
|
47
|
+
return filterValue.some((val) => String(val) === String(cellValue));
|
|
48
|
+
}
|
|
49
|
+
const cellValue = row.getValue(columnId);
|
|
50
|
+
if (cellValue == null) return false;
|
|
51
|
+
try {
|
|
52
|
+
const cellStr = String(cellValue).toLowerCase();
|
|
53
|
+
return getOrCreateRegex(String(filterValue).toLowerCase().replace(/[.*+?^${}()|[\]\\]/g, "\\$&"), "i").test(cellStr);
|
|
54
|
+
} catch {
|
|
55
|
+
return String(cellValue).toLowerCase().includes(String(filterValue).toLowerCase());
|
|
56
|
+
}
|
|
57
|
+
};
|
|
58
|
+
/**
|
|
59
|
+
* Global filter with operator precedence. Supports plain string search,
|
|
60
|
+
* pure OR, and mixed AND/OR (AND has higher precedence than OR — splits
|
|
61
|
+
* filters into OR-separated AND-groups).
|
|
62
|
+
*/
|
|
63
|
+
/**
|
|
64
|
+
* Whether a `globalFilter` value represents a search the reader actually made.
|
|
65
|
+
*
|
|
66
|
+
* A whitespace-only string does not: it is invisible in the search input, so
|
|
67
|
+
* treating it as active makes the UI insist something is filtered while showing
|
|
68
|
+
* nothing that could be. The advanced filter payload is an object and always
|
|
69
|
+
* counts.
|
|
70
|
+
*/
|
|
71
|
+
function isGlobalFilterActive(value) {
|
|
72
|
+
if (typeof value === "string") return value.trim().length > 0;
|
|
73
|
+
return Boolean(value);
|
|
74
|
+
}
|
|
75
|
+
const globalFilter = (row, _columnId, filterValue, _addMeta) => {
|
|
76
|
+
if (!filterValue) return true;
|
|
77
|
+
if (typeof filterValue === "object" && filterValue.filters && Array.isArray(filterValue.filters)) {
|
|
78
|
+
const filters = filterValue.filters;
|
|
79
|
+
if (filterValue.joinOperator === "or") return filters.some((filter) => {
|
|
80
|
+
return applyFilterOperator(row.getValue(filter.id), filter.operator, filter.value);
|
|
81
|
+
});
|
|
82
|
+
else if (filterValue.joinOperator === JOIN_OPERATORS.MIXED) {
|
|
83
|
+
if (filters.length === 0) return true;
|
|
84
|
+
if (filters.length === 1) {
|
|
85
|
+
const filter = filters[0];
|
|
86
|
+
return applyFilterOperator(row.getValue(filter.id), filter.operator, filter.value);
|
|
87
|
+
}
|
|
88
|
+
const orGroups = [];
|
|
89
|
+
let currentAndGroup = [];
|
|
90
|
+
currentAndGroup.push(filters[0]);
|
|
91
|
+
for (let i = 1; i < filters.length; i++) {
|
|
92
|
+
const filter = filters[i];
|
|
93
|
+
if (filter.joinOperator === JOIN_OPERATORS.OR) {
|
|
94
|
+
orGroups.push(currentAndGroup);
|
|
95
|
+
currentAndGroup = [filter];
|
|
96
|
+
} else currentAndGroup.push(filter);
|
|
97
|
+
}
|
|
98
|
+
orGroups.push(currentAndGroup);
|
|
99
|
+
return orGroups.map((andGroup) => {
|
|
100
|
+
return andGroup.every((filter) => {
|
|
101
|
+
return applyFilterOperator(row.getValue(filter.id), filter.operator, filter.value);
|
|
102
|
+
});
|
|
103
|
+
}).some((result) => result);
|
|
104
|
+
}
|
|
105
|
+
return filters.every((filter) => {
|
|
106
|
+
return applyFilterOperator(row.getValue(filter.id), filter.operator, filter.value);
|
|
107
|
+
});
|
|
108
|
+
}
|
|
109
|
+
const searchValue = String(filterValue).trim().toLowerCase();
|
|
110
|
+
if (searchValue === "") return true;
|
|
111
|
+
return row.getAllCells().some((cell) => {
|
|
112
|
+
if (cell.column.getCanFilter() === false) return false;
|
|
113
|
+
const cellValue = cell.getValue();
|
|
114
|
+
if (cellValue == null) return false;
|
|
115
|
+
try {
|
|
116
|
+
const cellStr = String(cellValue).toLowerCase();
|
|
117
|
+
return getOrCreateRegex(searchValue.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"), "i").test(cellStr);
|
|
118
|
+
} catch {
|
|
119
|
+
return String(cellValue).toLowerCase().includes(searchValue);
|
|
120
|
+
}
|
|
121
|
+
});
|
|
122
|
+
};
|
|
123
|
+
/**
|
|
124
|
+
* Apply filter operator to a cell value
|
|
125
|
+
*/
|
|
126
|
+
function applyFilterOperator(cellValue, operator, filterValue) {
|
|
127
|
+
if (cellValue == null) switch (operator) {
|
|
128
|
+
case FILTER_OPERATORS.EMPTY: return true;
|
|
129
|
+
case FILTER_OPERATORS.NOT_EMPTY: return false;
|
|
130
|
+
default: return false;
|
|
131
|
+
}
|
|
132
|
+
const cellStr = String(cellValue).toLowerCase();
|
|
133
|
+
const filterStr = String(filterValue).toLowerCase();
|
|
134
|
+
switch (operator) {
|
|
135
|
+
case FILTER_OPERATORS.ILIKE: try {
|
|
136
|
+
return getOrCreateRegex(filterStr.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"), "i").test(cellStr);
|
|
137
|
+
} catch {
|
|
138
|
+
return cellStr.includes(filterStr);
|
|
139
|
+
}
|
|
140
|
+
case FILTER_OPERATORS.NOT_ILIKE: try {
|
|
141
|
+
return !getOrCreateRegex(filterStr.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"), "i").test(cellStr);
|
|
142
|
+
} catch {
|
|
143
|
+
return !cellStr.includes(filterStr);
|
|
144
|
+
}
|
|
145
|
+
case FILTER_OPERATORS.EQ:
|
|
146
|
+
if (typeof cellValue === "string" && typeof filterValue === "string") return cellStr === filterStr;
|
|
147
|
+
if (typeof cellValue === "boolean") return String(cellValue) === String(filterValue);
|
|
148
|
+
if (typeof filterValue === "boolean") return String(filterValue) === String(cellValue);
|
|
149
|
+
if (typeof cellValue === "object" && cellValue !== null && "getTime" in cellValue) {
|
|
150
|
+
const dateCell = cellValue.getTime();
|
|
151
|
+
const dateFilter = Number(filterValue);
|
|
152
|
+
if (!isNaN(dateCell) && !isNaN(dateFilter)) return new Date(dateCell).setHours(0, 0, 0, 0) === new Date(dateFilter).setHours(0, 0, 0, 0);
|
|
153
|
+
}
|
|
154
|
+
if (typeof cellValue === "number" || typeof filterValue === "number") {
|
|
155
|
+
const numCell = Number(cellValue);
|
|
156
|
+
const numFilter = Number(filterValue);
|
|
157
|
+
if (!isNaN(numCell) && !isNaN(numFilter)) return numCell === numFilter;
|
|
158
|
+
}
|
|
159
|
+
return cellValue === filterValue;
|
|
160
|
+
case FILTER_OPERATORS.NEQ:
|
|
161
|
+
if (typeof cellValue === "string" && typeof filterValue === "string") return cellStr !== filterStr;
|
|
162
|
+
if (typeof cellValue === "object" && cellValue !== null && "getTime" in cellValue) {
|
|
163
|
+
const dateCell = cellValue.getTime();
|
|
164
|
+
const dateFilter = Number(filterValue);
|
|
165
|
+
if (!isNaN(dateCell) && !isNaN(dateFilter)) return new Date(dateCell).setHours(0, 0, 0, 0) !== new Date(dateFilter).setHours(0, 0, 0, 0);
|
|
166
|
+
}
|
|
167
|
+
if (typeof cellValue === "number" || typeof filterValue === "number") {
|
|
168
|
+
const numCell = Number(cellValue);
|
|
169
|
+
const numFilter = Number(filterValue);
|
|
170
|
+
if (!isNaN(numCell) && !isNaN(numFilter)) return numCell !== numFilter;
|
|
171
|
+
}
|
|
172
|
+
return cellValue !== filterValue;
|
|
173
|
+
case FILTER_OPERATORS.EMPTY:
|
|
174
|
+
if (typeof cellValue === "string") return cellValue.trim() === "";
|
|
175
|
+
return cellValue == null;
|
|
176
|
+
case FILTER_OPERATORS.NOT_EMPTY:
|
|
177
|
+
if (typeof cellValue === "string") return cellValue.trim() !== "";
|
|
178
|
+
return cellValue != null;
|
|
179
|
+
case FILTER_OPERATORS.LT: {
|
|
180
|
+
const numCell = Number(cellValue);
|
|
181
|
+
const numFilter = Number(filterValue);
|
|
182
|
+
if (isNaN(numCell) || isNaN(numFilter)) return false;
|
|
183
|
+
return numCell < numFilter;
|
|
184
|
+
}
|
|
185
|
+
case FILTER_OPERATORS.LTE: {
|
|
186
|
+
const numCell = Number(cellValue);
|
|
187
|
+
const numFilter = Number(filterValue);
|
|
188
|
+
if (isNaN(numCell) || isNaN(numFilter)) return false;
|
|
189
|
+
return numCell <= numFilter;
|
|
190
|
+
}
|
|
191
|
+
case FILTER_OPERATORS.GT: {
|
|
192
|
+
const numCell = Number(cellValue);
|
|
193
|
+
const numFilter = Number(filterValue);
|
|
194
|
+
if (isNaN(numCell) || isNaN(numFilter)) return false;
|
|
195
|
+
return numCell > numFilter;
|
|
196
|
+
}
|
|
197
|
+
case FILTER_OPERATORS.GTE: {
|
|
198
|
+
const numCell = Number(cellValue);
|
|
199
|
+
const numFilter = Number(filterValue);
|
|
200
|
+
if (isNaN(numCell) || isNaN(numFilter)) return false;
|
|
201
|
+
return numCell >= numFilter;
|
|
202
|
+
}
|
|
203
|
+
case FILTER_OPERATORS.BETWEEN:
|
|
204
|
+
if (Array.isArray(filterValue) && filterValue.length === 2) {
|
|
205
|
+
const [min, max] = filterValue;
|
|
206
|
+
const numValue = Number(cellValue);
|
|
207
|
+
const numMin = Number(min);
|
|
208
|
+
const numMax = Number(max);
|
|
209
|
+
if (isNaN(numValue) || isNaN(numMin) || isNaN(numMax)) return false;
|
|
210
|
+
return numValue >= numMin && numValue <= numMax;
|
|
211
|
+
}
|
|
212
|
+
return false;
|
|
213
|
+
case FILTER_OPERATORS.IN:
|
|
214
|
+
if (Array.isArray(filterValue)) {
|
|
215
|
+
if (typeof cellValue === "string") {
|
|
216
|
+
const cellLower = cellValue.toLowerCase();
|
|
217
|
+
return filterValue.some((val) => typeof val === "string" ? val.toLowerCase() === cellLower : val === cellValue);
|
|
218
|
+
}
|
|
219
|
+
return filterValue.some((val) => String(val) === String(cellValue));
|
|
220
|
+
}
|
|
221
|
+
return false;
|
|
222
|
+
case FILTER_OPERATORS.NOT_IN:
|
|
223
|
+
if (Array.isArray(filterValue)) {
|
|
224
|
+
if (typeof cellValue === "string") {
|
|
225
|
+
const cellLower = cellValue.toLowerCase();
|
|
226
|
+
return !filterValue.some((val) => typeof val === "string" ? val.toLowerCase() === cellLower : val === cellValue);
|
|
227
|
+
}
|
|
228
|
+
return !filterValue.some((val) => String(val) === String(cellValue));
|
|
229
|
+
}
|
|
230
|
+
return true;
|
|
231
|
+
case FILTER_OPERATORS.RELATIVE: throw new Error("FILTER_OPERATORS.RELATIVE is not yet implemented. Either remove the 'Is relative to today' option from the date filter UI or implement this case.");
|
|
232
|
+
default: try {
|
|
233
|
+
return getOrCreateRegex(filterStr.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"), "i").test(cellStr);
|
|
234
|
+
} catch {
|
|
235
|
+
return cellStr.includes(filterStr);
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
/**
|
|
240
|
+
* Filter function for number range (slider) filters
|
|
241
|
+
* Handles array values [min, max] for range filtering
|
|
242
|
+
*/
|
|
243
|
+
const numberRangeFilter = (row, columnId, filterValue, addMeta) => {
|
|
244
|
+
if (!filterValue) return true;
|
|
245
|
+
if (typeof filterValue === "object" && filterValue.operator && filterValue.value !== void 0) {
|
|
246
|
+
const filter = filterValue;
|
|
247
|
+
return applyFilterOperator(row.getValue(columnId), filter.operator, filter.value);
|
|
248
|
+
}
|
|
249
|
+
if (Array.isArray(filterValue) && filterValue.length === 2) {
|
|
250
|
+
const [min, max] = filterValue;
|
|
251
|
+
const value = Number(row.getValue(columnId));
|
|
252
|
+
if (isNaN(value)) return false;
|
|
253
|
+
const numMin = Number(min);
|
|
254
|
+
const numMax = Number(max);
|
|
255
|
+
if (isNaN(numMin) || isNaN(numMax)) return false;
|
|
256
|
+
return value >= numMin && value <= numMax;
|
|
257
|
+
}
|
|
258
|
+
return extendedFilter(row, columnId, filterValue, addMeta);
|
|
259
|
+
};
|
|
260
|
+
/**
|
|
261
|
+
* Filter function for date range filters
|
|
262
|
+
* Handles both single date (timestamp) and date range [from, to] (timestamps)
|
|
263
|
+
*/
|
|
264
|
+
const dateRangeFilter = (row, columnId, filterValue, addMeta) => {
|
|
265
|
+
if (!filterValue) return true;
|
|
266
|
+
if (typeof filterValue === "object" && filterValue.operator && filterValue.value !== void 0) {
|
|
267
|
+
const filter = filterValue;
|
|
268
|
+
return applyFilterOperator(row.getValue(columnId), filter.operator, filter.value);
|
|
269
|
+
}
|
|
270
|
+
const rowValue = row.getValue(columnId);
|
|
271
|
+
if (!rowValue) return false;
|
|
272
|
+
const rowTimestamp = rowValue instanceof Date ? rowValue.getTime() : typeof rowValue === "number" ? rowValue : new Date(rowValue).getTime();
|
|
273
|
+
if (isNaN(rowTimestamp)) return false;
|
|
274
|
+
if (Array.isArray(filterValue)) {
|
|
275
|
+
if (filterValue.length === 2) {
|
|
276
|
+
const [from, to] = filterValue;
|
|
277
|
+
const fromTime = Number(from);
|
|
278
|
+
const toTime = Number(to);
|
|
279
|
+
if (isNaN(fromTime) || isNaN(toTime)) return false;
|
|
280
|
+
return rowTimestamp >= fromTime && rowTimestamp <= toTime;
|
|
281
|
+
}
|
|
282
|
+
if (filterValue.length === 1) {
|
|
283
|
+
const dateTime = Number(filterValue[0]);
|
|
284
|
+
if (isNaN(dateTime)) return false;
|
|
285
|
+
return new Date(rowTimestamp).setHours(0, 0, 0, 0) === new Date(dateTime).setHours(0, 0, 0, 0);
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
if (typeof filterValue === "number") return new Date(rowTimestamp).setHours(0, 0, 0, 0) === new Date(filterValue).setHours(0, 0, 0, 0);
|
|
289
|
+
return extendedFilter(row, columnId, filterValue, addMeta);
|
|
290
|
+
};
|
|
291
|
+
/**
|
|
292
|
+
* Helper function to create filter value with operator
|
|
293
|
+
*
|
|
294
|
+
* @param operator - The filter operator to apply
|
|
295
|
+
* @param value - The value to filter by
|
|
296
|
+
* @returns ExtendedColumnFilter object with default properties
|
|
297
|
+
*/
|
|
298
|
+
const createFilterValue = (operator, value) => {
|
|
299
|
+
return {
|
|
300
|
+
id: "",
|
|
301
|
+
filterId: "",
|
|
302
|
+
operator,
|
|
303
|
+
value,
|
|
304
|
+
variant: FILTER_VARIANTS.TEXT,
|
|
305
|
+
joinOperator: JOIN_OPERATORS.AND
|
|
306
|
+
};
|
|
307
|
+
};
|
|
308
|
+
//#endregion
|
|
309
|
+
export { createFilterValue, dateRangeFilter, extendedFilter, globalFilter, isGlobalFilterActive, numberRangeFilter };
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { DataTableInstance, DataTableRow } from "../types/index.js";
|
|
2
|
+
import { RowData } from "@tanstack/react-table";
|
|
3
|
+
//#region src/components/registry/niko-table/lib/filter-rows.d.ts
|
|
4
|
+
/**
|
|
5
|
+
* Get filtered rows excluding a specific column's filter.
|
|
6
|
+
* This is useful when generating options for a column - we want to see
|
|
7
|
+
* options that exist in the filtered dataset (from other filters) but
|
|
8
|
+
* not be limited by the current column's own filter.
|
|
9
|
+
*/
|
|
10
|
+
export declare function getFilteredRowsExcludingColumn<TData extends RowData>(table: DataTableInstance<TData>, coreRows: DataTableRow<TData>[], excludeColumnId: string, columnFilters: Array<{
|
|
11
|
+
id: string;
|
|
12
|
+
value: unknown;
|
|
13
|
+
}>, globalFilter: unknown): DataTableRow<TData>[];
|
|
14
|
+
//#endregion
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
//#region src/components/registry/niko-table/lib/filter-rows.ts
|
|
2
|
+
/**
|
|
3
|
+
* Get filtered rows excluding a specific column's filter.
|
|
4
|
+
* This is useful when generating options for a column - we want to see
|
|
5
|
+
* options that exist in the filtered dataset (from other filters) but
|
|
6
|
+
* not be limited by the current column's own filter.
|
|
7
|
+
*/
|
|
8
|
+
function getFilteredRowsExcludingColumn(table, coreRows, excludeColumnId, columnFilters, globalFilter) {
|
|
9
|
+
const otherFilters = columnFilters.filter((filter) => filter.id !== excludeColumnId);
|
|
10
|
+
if (otherFilters.length === 0 && !globalFilter) return coreRows;
|
|
11
|
+
const columnIds = new Set(table.getAllLeafColumns().map((c) => c.id));
|
|
12
|
+
return coreRows.filter((row) => {
|
|
13
|
+
for (const filter of otherFilters) {
|
|
14
|
+
if (!columnIds.has(filter.id)) continue;
|
|
15
|
+
const column = table.getColumn(filter.id);
|
|
16
|
+
if (!column) continue;
|
|
17
|
+
const filterFn = column.getFilterFn();
|
|
18
|
+
if (filterFn && !filterFn(row, filter.id, filter.value, () => {})) return false;
|
|
19
|
+
}
|
|
20
|
+
if (globalFilter) {
|
|
21
|
+
const globalFilterFn = table.getGlobalFilterFn();
|
|
22
|
+
if (globalFilterFn && !globalFilterFn(row, "global", globalFilter, () => {})) return false;
|
|
23
|
+
}
|
|
24
|
+
return true;
|
|
25
|
+
});
|
|
26
|
+
}
|
|
27
|
+
//#endregion
|
|
28
|
+
export { getFilteredRowsExcludingColumn };
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { DataTableColumn, DataTableInstance } from "../types/index.js";
|
|
2
|
+
import { ColumnSizingState, RowData } from "@tanstack/react-table";
|
|
3
|
+
//#region src/components/registry/niko-table/lib/flex-columns.d.ts
|
|
4
|
+
/**
|
|
5
|
+
* Resolve which column(s) should flex to fill the leftover row width.
|
|
6
|
+
*
|
|
7
|
+
* Flex fill is ON BY DEFAULT for resizable tables. A flex column renders with
|
|
8
|
+
* no explicit width, so under `table-layout: fixed` it soaks up the surplus:
|
|
9
|
+
* the table fills its container and a trailing actions column pins to the right
|
|
10
|
+
* edge instead of leaving dead space. It's pure layout — never writes
|
|
11
|
+
* `columnSizing`, so no persistence side effects.
|
|
12
|
+
*
|
|
13
|
+
* Selection: when no column opts in explicitly, the DEFAULT is the first
|
|
14
|
+
* non-pinned, resizable column (the primary text column). Selection / expand /
|
|
15
|
+
* actions columns are non-resizable (`enableResizing: false`), so they're
|
|
16
|
+
* skipped automatically; the default lands on the first real data column.
|
|
17
|
+
*
|
|
18
|
+
* Overrides:
|
|
19
|
+
* - `meta: { flex: true }` on a column flexes THAT column instead of the
|
|
20
|
+
* default — use it when the primary column isn't the first one (e.g. a table
|
|
21
|
+
* whose first columns are narrow codes/dates and a later column should grow).
|
|
22
|
+
* - `meta: { flex: false }` opts a column out of ever being auto-picked.
|
|
23
|
+
* - `meta: { disableFlexFill: true }` on the table (TableMeta) turns fill off
|
|
24
|
+
* entirely — for a wide table meant to scroll horizontally rather than fill.
|
|
25
|
+
*
|
|
26
|
+
* Returns the set of column ids to render widthless (empty when fill is off or
|
|
27
|
+
* no eligible column exists — the table then sizes to its columns as before).
|
|
28
|
+
*/
|
|
29
|
+
export declare function resolveFlexColumnIds<TData extends RowData>(table: DataTableInstance<TData>): ReadonlySet<string>;
|
|
30
|
+
/**
|
|
31
|
+
* The width to render for a single column. Centralizes the flex + header-fit
|
|
32
|
+
* rules so the header, body cells, and the min-width lock all agree.
|
|
33
|
+
*
|
|
34
|
+
* - Off (no resizing): the declared `size` (or auto when unset).
|
|
35
|
+
* - Flex column: no width — it fills the leftover row width.
|
|
36
|
+
* - Resized by the user: exactly `getSize()` — their choice wins, even if it's
|
|
37
|
+
* narrower than the header (matches how spreadsheet grids let you shrink).
|
|
38
|
+
* - Otherwise: floored at the header's natural width (`headerMinWidths`) so the
|
|
39
|
+
* label never truncates on load. Pure layout — `headerMinWidths` is measured,
|
|
40
|
+
* never written back into `columnSizing`, so nothing persists or goes stale.
|
|
41
|
+
*/
|
|
42
|
+
export declare function resolveColumnWidth<TData extends RowData>(column: DataTableColumn<TData>, params: {
|
|
43
|
+
resizing: boolean;
|
|
44
|
+
isFlex: boolean;
|
|
45
|
+
columnSizing: ColumnSizingState;
|
|
46
|
+
headerMinWidths: ReadonlyMap<string, number>;
|
|
47
|
+
}): number | string | undefined;
|
|
48
|
+
//#endregion
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
//#region src/components/registry/niko-table/lib/flex-columns.ts
|
|
2
|
+
/**
|
|
3
|
+
* Resolve which column(s) should flex to fill the leftover row width.
|
|
4
|
+
*
|
|
5
|
+
* Flex fill is ON BY DEFAULT for resizable tables. A flex column renders with
|
|
6
|
+
* no explicit width, so under `table-layout: fixed` it soaks up the surplus:
|
|
7
|
+
* the table fills its container and a trailing actions column pins to the right
|
|
8
|
+
* edge instead of leaving dead space. It's pure layout — never writes
|
|
9
|
+
* `columnSizing`, so no persistence side effects.
|
|
10
|
+
*
|
|
11
|
+
* Selection: when no column opts in explicitly, the DEFAULT is the first
|
|
12
|
+
* non-pinned, resizable column (the primary text column). Selection / expand /
|
|
13
|
+
* actions columns are non-resizable (`enableResizing: false`), so they're
|
|
14
|
+
* skipped automatically; the default lands on the first real data column.
|
|
15
|
+
*
|
|
16
|
+
* Overrides:
|
|
17
|
+
* - `meta: { flex: true }` on a column flexes THAT column instead of the
|
|
18
|
+
* default — use it when the primary column isn't the first one (e.g. a table
|
|
19
|
+
* whose first columns are narrow codes/dates and a later column should grow).
|
|
20
|
+
* - `meta: { flex: false }` opts a column out of ever being auto-picked.
|
|
21
|
+
* - `meta: { disableFlexFill: true }` on the table (TableMeta) turns fill off
|
|
22
|
+
* entirely — for a wide table meant to scroll horizontally rather than fill.
|
|
23
|
+
*
|
|
24
|
+
* Returns the set of column ids to render widthless (empty when fill is off or
|
|
25
|
+
* no eligible column exists — the table then sizes to its columns as before).
|
|
26
|
+
*/
|
|
27
|
+
function resolveFlexColumnIds(table) {
|
|
28
|
+
const ids = /* @__PURE__ */ new Set();
|
|
29
|
+
if (!table.options.enableColumnResizing) return ids;
|
|
30
|
+
if (table.options.meta?.disableFlexFill) return ids;
|
|
31
|
+
const leafColumns = table.getVisibleLeafColumns();
|
|
32
|
+
const columnSizing = table.state.columnSizing;
|
|
33
|
+
let hasExplicit = false;
|
|
34
|
+
for (const column of leafColumns) if (column.columnDef.meta?.flex === true && !column.getIsPinned() && columnSizing[column.id] == null) {
|
|
35
|
+
ids.add(column.id);
|
|
36
|
+
hasExplicit = true;
|
|
37
|
+
}
|
|
38
|
+
if (hasExplicit) return ids;
|
|
39
|
+
const auto = leafColumns.find((column) => column.getCanResize() && !column.getIsPinned() && column.columnDef.meta?.flex !== false && columnSizing[column.id] == null);
|
|
40
|
+
if (auto) ids.add(auto.id);
|
|
41
|
+
return ids;
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* The width to render for a single column. Centralizes the flex + header-fit
|
|
45
|
+
* rules so the header, body cells, and the min-width lock all agree.
|
|
46
|
+
*
|
|
47
|
+
* - Off (no resizing): the declared `size` (or auto when unset).
|
|
48
|
+
* - Flex column: no width — it fills the leftover row width.
|
|
49
|
+
* - Resized by the user: exactly `getSize()` — their choice wins, even if it's
|
|
50
|
+
* narrower than the header (matches how spreadsheet grids let you shrink).
|
|
51
|
+
* - Otherwise: floored at the header's natural width (`headerMinWidths`) so the
|
|
52
|
+
* label never truncates on load. Pure layout — `headerMinWidths` is measured,
|
|
53
|
+
* never written back into `columnSizing`, so nothing persists or goes stale.
|
|
54
|
+
*/
|
|
55
|
+
function resolveColumnWidth(column, params) {
|
|
56
|
+
const size = column.columnDef.size;
|
|
57
|
+
if (!params.resizing) return size ? `${size}px` : void 0;
|
|
58
|
+
if (params.isFlex) return void 0;
|
|
59
|
+
const base = column.getSize();
|
|
60
|
+
if (params.columnSizing[column.id] != null) return base;
|
|
61
|
+
const headerMin = params.headerMinWidths.get(column.id) ?? 0;
|
|
62
|
+
return headerMin > base ? headerMin : base;
|
|
63
|
+
}
|
|
64
|
+
//#endregion
|
|
65
|
+
export { resolveColumnWidth, resolveFlexColumnIds };
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
//#region src/components/registry/niko-table/lib/format.d.ts
|
|
2
|
+
export declare function formatDate(date: Date | string | number | undefined, opts?: Intl.DateTimeFormatOptions): string;
|
|
3
|
+
/**
|
|
4
|
+
* Format a value into a human-readable label.
|
|
5
|
+
* Capitalizes first letter of each word and replaces hyphens/underscores with spaces.
|
|
6
|
+
*
|
|
7
|
+
* @example
|
|
8
|
+
* formatLabel("firstName") // "FirstName"
|
|
9
|
+
* formatLabel("first-name") // "First Name"
|
|
10
|
+
* formatLabel("first_name") // "First Name"
|
|
11
|
+
* formatLabel("true") // "Yes"
|
|
12
|
+
* formatLabel("false") // "No"
|
|
13
|
+
*/
|
|
14
|
+
export declare function formatLabel(value: string): string;
|
|
15
|
+
export declare function daysAgo(days: number): Date;
|
|
16
|
+
/**
|
|
17
|
+
* Format URL query parameters into a human-readable query string for display.
|
|
18
|
+
* Decodes URL-encoded values and formats JSON objects in a readable way.
|
|
19
|
+
*
|
|
20
|
+
* @param urlParams - The parsed URL parameters object
|
|
21
|
+
* @param urlKeys - Mapping of parameter keys to URL query keys
|
|
22
|
+
* @returns Formatted query string (e.g., `?search=i&global={"filters":[...]}`)
|
|
23
|
+
*
|
|
24
|
+
* @example
|
|
25
|
+
* ```ts
|
|
26
|
+
* const urlParams = { search: "i", globalFilter: { filters: [...], joinOperator: "mixed" } }
|
|
27
|
+
* const urlKeys = { search: "search", globalFilter: "global" }
|
|
28
|
+
* formatQueryString(urlParams, urlKeys)
|
|
29
|
+
* // Returns: "?search=i&global={"filters":[...], "joinOperator":"mixed"}"
|
|
30
|
+
* ```
|
|
31
|
+
*/
|
|
32
|
+
export declare function formatQueryString(urlParams: Record<string, unknown>, urlKeys: Record<string, string>): string;
|
|
33
|
+
//#endregion
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
//#region src/components/registry/niko-table/lib/format.ts
|
|
2
|
+
function formatDate(date, opts = {}) {
|
|
3
|
+
if (!date) return "";
|
|
4
|
+
try {
|
|
5
|
+
return new Intl.DateTimeFormat("en-US", {
|
|
6
|
+
month: opts.month ?? "long",
|
|
7
|
+
day: opts.day ?? "numeric",
|
|
8
|
+
year: opts.year ?? "numeric",
|
|
9
|
+
...opts
|
|
10
|
+
}).format(new Date(date));
|
|
11
|
+
} catch {
|
|
12
|
+
return "";
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Format a value into a human-readable label.
|
|
17
|
+
* Capitalizes first letter of each word and replaces hyphens/underscores with spaces.
|
|
18
|
+
*
|
|
19
|
+
* @example
|
|
20
|
+
* formatLabel("firstName") // "FirstName"
|
|
21
|
+
* formatLabel("first-name") // "First Name"
|
|
22
|
+
* formatLabel("first_name") // "First Name"
|
|
23
|
+
* formatLabel("true") // "Yes"
|
|
24
|
+
* formatLabel("false") // "No"
|
|
25
|
+
*/
|
|
26
|
+
function formatLabel(value) {
|
|
27
|
+
if (value === "true") return "Yes";
|
|
28
|
+
if (value === "false") return "No";
|
|
29
|
+
return value.replace(/[-_]/g, " ").split(" ").map((word) => word.charAt(0).toUpperCase() + word.slice(1)).join(" ");
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Create a date relative to a fixed epoch by subtracting days.
|
|
33
|
+
* Deterministic — safe for mock data / SSR hydration (no `Date.now()`).
|
|
34
|
+
*
|
|
35
|
+
* @param days - Number of days to subtract from the mock epoch
|
|
36
|
+
* @returns Date object representing the date N days before the epoch
|
|
37
|
+
*
|
|
38
|
+
* @example
|
|
39
|
+
* daysAgo(7) // 7 days before 2026-07-15
|
|
40
|
+
* daysAgo(30) // 30 days before 2026-07-15
|
|
41
|
+
*/
|
|
42
|
+
const MOCK_EPOCH = /* @__PURE__ */ new Date("2026-07-15T12:00:00.000Z");
|
|
43
|
+
function daysAgo(days) {
|
|
44
|
+
const date = new Date(MOCK_EPOCH);
|
|
45
|
+
date.setUTCDate(date.getUTCDate() - days);
|
|
46
|
+
return date;
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Format URL query parameters into a human-readable query string for display.
|
|
50
|
+
* Decodes URL-encoded values and formats JSON objects in a readable way.
|
|
51
|
+
*
|
|
52
|
+
* @param urlParams - The parsed URL parameters object
|
|
53
|
+
* @param urlKeys - Mapping of parameter keys to URL query keys
|
|
54
|
+
* @returns Formatted query string (e.g., `?search=i&global={"filters":[...]}`)
|
|
55
|
+
*
|
|
56
|
+
* @example
|
|
57
|
+
* ```ts
|
|
58
|
+
* const urlParams = { search: "i", globalFilter: { filters: [...], joinOperator: "mixed" } }
|
|
59
|
+
* const urlKeys = { search: "search", globalFilter: "global" }
|
|
60
|
+
* formatQueryString(urlParams, urlKeys)
|
|
61
|
+
* // Returns: "?search=i&global={"filters":[...], "joinOperator":"mixed"}"
|
|
62
|
+
* ```
|
|
63
|
+
*/
|
|
64
|
+
function formatQueryString(urlParams, urlKeys) {
|
|
65
|
+
const parts = [];
|
|
66
|
+
const formatJson = (obj, showFull = false) => {
|
|
67
|
+
try {
|
|
68
|
+
if (showFull) return JSON.stringify(obj);
|
|
69
|
+
const str = JSON.stringify(obj);
|
|
70
|
+
if (str.length <= 80) return str;
|
|
71
|
+
if (Array.isArray(obj) && obj.length > 0) return `[{...}] (${obj.length} items)`;
|
|
72
|
+
if (typeof obj === "object" && obj !== null) {
|
|
73
|
+
const keys = Object.keys(obj);
|
|
74
|
+
if (keys.length > 0) {
|
|
75
|
+
const firstKey = keys[0] ?? "";
|
|
76
|
+
const firstValue = obj[firstKey];
|
|
77
|
+
if (Array.isArray(firstValue)) return `{${firstKey}: [...], ...}`;
|
|
78
|
+
if (typeof firstValue === "object" && firstValue !== null) return `{${firstKey}: {...}, ...}`;
|
|
79
|
+
return `{${firstKey}: ${String(firstValue)}, ...}`;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
return str.length > 100 ? `${str.slice(0, 100)}...` : str;
|
|
83
|
+
} catch {
|
|
84
|
+
return String(obj);
|
|
85
|
+
}
|
|
86
|
+
};
|
|
87
|
+
if (urlParams.pageIndex !== void 0 && urlParams.pageIndex !== 0) parts.push(`${urlKeys.pageIndex}=${urlParams.pageIndex}`);
|
|
88
|
+
if (urlParams.pageSize !== void 0 && urlParams.pageSize !== 10) parts.push(`${urlKeys.pageSize}=${urlParams.pageSize}`);
|
|
89
|
+
if (urlParams.sort && Array.isArray(urlParams.sort) && urlParams.sort.length > 0) parts.push(`${urlKeys.sort}=${formatJson(urlParams.sort)}`);
|
|
90
|
+
if (urlParams.filters && Array.isArray(urlParams.filters) && urlParams.filters.length > 0) parts.push(`${urlKeys.filters}=${formatJson(urlParams.filters, true)}`);
|
|
91
|
+
if (urlParams.search && typeof urlParams.search === "string") parts.push(`${urlKeys.search}=${urlParams.search}`);
|
|
92
|
+
if (urlParams.globalFilter && typeof urlParams.globalFilter === "object" && urlParams.globalFilter !== null && "filters" in urlParams.globalFilter) parts.push(`${urlKeys.globalFilter}=${formatJson(urlParams.globalFilter, true)}`);
|
|
93
|
+
if (urlParams.columnVisibility && typeof urlParams.columnVisibility === "object" && urlParams.columnVisibility !== null && Object.keys(urlParams.columnVisibility).length > 0) parts.push(`${urlKeys.columnVisibility}=${formatJson(urlParams.columnVisibility)}`);
|
|
94
|
+
if (urlParams.inlineFilters && Array.isArray(urlParams.inlineFilters) && urlParams.inlineFilters.length > 0) parts.push(`${urlKeys.inlineFilters}=${formatJson(urlParams.inlineFilters)}`);
|
|
95
|
+
if (urlParams.filterMode && urlParams.filterMode !== "standard") parts.push(`${urlKeys.filterMode}=${urlParams.filterMode}`);
|
|
96
|
+
return parts.length > 0 ? `?${parts.join("&")}` : "No query params";
|
|
97
|
+
}
|
|
98
|
+
//#endregion
|
|
99
|
+
export { daysAgo, formatDate, formatLabel, formatQueryString };
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { DataTableCell } from "../types/index.js";
|
|
2
|
+
import { ReactNode } from "react";
|
|
3
|
+
import { RowData } from "@tanstack/react-table";
|
|
4
|
+
//#region src/components/registry/niko-table/lib/render-cell-content.d.ts
|
|
5
|
+
/**
|
|
6
|
+
* Renders a body cell with TanStack grouping / aggregation awareness.
|
|
7
|
+
*
|
|
8
|
+
* - Grouped column cell: expand/collapse control + value + leaf count
|
|
9
|
+
* - Aggregated cell: `aggregatedCell` (falls back to `cell`)
|
|
10
|
+
* - Placeholder cell: empty (grouped columns leave gaps on leaf rows)
|
|
11
|
+
* - Otherwise: normal `cell` render
|
|
12
|
+
*
|
|
13
|
+
* Used by every body structure so grouping works the same for regular,
|
|
14
|
+
* virtualized, and DnD tables.
|
|
15
|
+
*/
|
|
16
|
+
export declare function renderCellContent<TData extends RowData, TValue>(cell: DataTableCell<TData, TValue>): ReactNode;
|
|
17
|
+
//#endregion
|