@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.
Files changed (192) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +174 -2
  3. package/THIRD_PARTY_NOTICES.md +51 -0
  4. package/dist/components/registry/dashboard-01/section-cards.d.ts +17 -0
  5. package/dist/components/registry/dashboard-01/section-cards.js +39 -0
  6. package/dist/components/registry/dashboardcn/bar-list.d.ts +24 -0
  7. package/dist/components/registry/dashboardcn/bar-list.js +71 -0
  8. package/dist/components/registry/heatmap/calendar-heatmap.d.ts +134 -0
  9. package/dist/components/registry/heatmap/calendar-heatmap.js +497 -0
  10. package/dist/components/registry/heatmap/status-heatmap.d.ts +120 -0
  11. package/dist/components/registry/heatmap/status-heatmap.js +330 -0
  12. package/dist/components/registry/heatmap/weekday-heatmap.d.ts +145 -0
  13. package/dist/components/registry/heatmap/weekday-heatmap.js +489 -0
  14. package/dist/components/registry/niko-table/components/data-table-column-actions.d.ts +26 -0
  15. package/dist/components/registry/niko-table/components/data-table-column-actions.js +32 -0
  16. package/dist/components/registry/niko-table/components/data-table-column-filter.d.ts +13 -0
  17. package/dist/components/registry/niko-table/components/data-table-column-filter.js +17 -0
  18. package/dist/components/registry/niko-table/components/data-table-column-header.d.ts +30 -0
  19. package/dist/components/registry/niko-table/components/data-table-column-header.js +35 -0
  20. package/dist/components/registry/niko-table/components/data-table-column-sort.d.ts +21 -0
  21. package/dist/components/registry/niko-table/components/data-table-column-sort.js +36 -0
  22. package/dist/components/registry/niko-table/components/data-table-column-title.d.ts +12 -0
  23. package/dist/components/registry/niko-table/components/data-table-column-title.js +18 -0
  24. package/dist/components/registry/niko-table/components/data-table-empty-state.d.ts +108 -0
  25. package/dist/components/registry/niko-table/components/data-table-empty-state.js +148 -0
  26. package/dist/components/registry/niko-table/components/data-table-faceted-filter.d.ts +46 -0
  27. package/dist/components/registry/niko-table/components/data-table-faceted-filter.js +133 -0
  28. package/dist/components/registry/niko-table/components/data-table-grouped-rows.d.ts +111 -0
  29. package/dist/components/registry/niko-table/components/data-table-grouped-rows.js +77 -0
  30. package/dist/components/registry/niko-table/components/data-table-row-context-menu-slot.d.ts +65 -0
  31. package/dist/components/registry/niko-table/components/data-table-row-context-menu-slot.js +65 -0
  32. package/dist/components/registry/niko-table/components/data-table-row-context-menu.d.ts +50 -0
  33. package/dist/components/registry/niko-table/components/data-table-row-context-menu.js +42 -0
  34. package/dist/components/registry/niko-table/components/data-table-row-menu.d.ts +51 -0
  35. package/dist/components/registry/niko-table/components/data-table-row-menu.js +65 -0
  36. package/dist/components/registry/niko-table/components/data-table-toolbar-section.d.ts +7 -0
  37. package/dist/components/registry/niko-table/components/data-table-toolbar-section.js +63 -0
  38. package/dist/components/registry/niko-table/config/data-table.d.ts +156 -0
  39. package/dist/components/registry/niko-table/config/data-table.js +279 -0
  40. package/dist/components/registry/niko-table/config/feature-detection.d.ts +39 -0
  41. package/dist/components/registry/niko-table/config/feature-detection.js +174 -0
  42. package/dist/components/registry/niko-table/core/data-table-context.d.ts +157 -0
  43. package/dist/components/registry/niko-table/core/data-table-context.js +326 -0
  44. package/dist/components/registry/niko-table/core/data-table-error-boundary.d.ts +85 -0
  45. package/dist/components/registry/niko-table/core/data-table-error-boundary.js +116 -0
  46. package/dist/components/registry/niko-table/core/data-table-grouped-row.d.ts +49 -0
  47. package/dist/components/registry/niko-table/core/data-table-grouped-row.js +112 -0
  48. package/dist/components/registry/niko-table/core/data-table-root.d.ts +62 -0
  49. package/dist/components/registry/niko-table/core/data-table-root.js +406 -0
  50. package/dist/components/registry/niko-table/core/data-table-structure.d.ts +153 -0
  51. package/dist/components/registry/niko-table/core/data-table-structure.js +370 -0
  52. package/dist/components/registry/niko-table/core/data-table.d.ts +30 -0
  53. package/dist/components/registry/niko-table/core/data-table.js +126 -0
  54. package/dist/components/registry/niko-table/filters/table-column-actions.d.ts +49 -0
  55. package/dist/components/registry/niko-table/filters/table-column-actions.js +48 -0
  56. package/dist/components/registry/niko-table/filters/table-column-sort.d.ts +51 -0
  57. package/dist/components/registry/niko-table/filters/table-column-sort.js +184 -0
  58. package/dist/components/registry/niko-table/filters/table-column-title.d.ts +17 -0
  59. package/dist/components/registry/niko-table/filters/table-column-title.js +20 -0
  60. package/dist/components/registry/niko-table/filters/table-faceted-filter.d.ts +40 -0
  61. package/dist/components/registry/niko-table/filters/table-faceted-filter.js +210 -0
  62. package/dist/components/registry/niko-table/hooks/use-derived-column-title.d.ts +29 -0
  63. package/dist/components/registry/niko-table/hooks/use-derived-column-title.js +40 -0
  64. package/dist/components/registry/niko-table/hooks/use-generated-options.d.ts +49 -0
  65. package/dist/components/registry/niko-table/hooks/use-generated-options.js +144 -0
  66. package/dist/components/registry/niko-table/lib/build-faceted-options.d.ts +59 -0
  67. package/dist/components/registry/niko-table/lib/build-faceted-options.js +82 -0
  68. package/dist/components/registry/niko-table/lib/column-resize-handle.d.ts +33 -0
  69. package/dist/components/registry/niko-table/lib/column-resize-handle.js +175 -0
  70. package/dist/components/registry/niko-table/lib/constants.d.ts +128 -0
  71. package/dist/components/registry/niko-table/lib/constants.js +123 -0
  72. package/dist/components/registry/niko-table/lib/create-scroll-handler.d.ts +35 -0
  73. package/dist/components/registry/niko-table/lib/create-scroll-handler.js +63 -0
  74. package/dist/components/registry/niko-table/lib/data-table-features.d.ts +218 -0
  75. package/dist/components/registry/niko-table/lib/data-table-features.js +127 -0
  76. package/dist/components/registry/niko-table/lib/filter-functions.d.ts +47 -0
  77. package/dist/components/registry/niko-table/lib/filter-functions.js +309 -0
  78. package/dist/components/registry/niko-table/lib/filter-rows.d.ts +14 -0
  79. package/dist/components/registry/niko-table/lib/filter-rows.js +28 -0
  80. package/dist/components/registry/niko-table/lib/flex-columns.d.ts +48 -0
  81. package/dist/components/registry/niko-table/lib/flex-columns.js +65 -0
  82. package/dist/components/registry/niko-table/lib/format.d.ts +33 -0
  83. package/dist/components/registry/niko-table/lib/format.js +99 -0
  84. package/dist/components/registry/niko-table/lib/render-cell-content.d.ts +17 -0
  85. package/dist/components/registry/niko-table/lib/render-cell-content.js +65 -0
  86. package/dist/components/registry/niko-table/lib/row-click.d.ts +67 -0
  87. package/dist/components/registry/niko-table/lib/row-click.js +28 -0
  88. package/dist/components/registry/niko-table/lib/styles.d.ts +6 -0
  89. package/dist/components/registry/niko-table/lib/styles.js +24 -0
  90. package/dist/components/registry/niko-table/lib/use-header-min-widths.d.ts +17 -0
  91. package/dist/components/registry/niko-table/lib/use-header-min-widths.js +82 -0
  92. package/dist/components/registry/niko-table/types/index.d.ts +141 -0
  93. package/dist/components/registry/niko-table/types/index.js +2 -0
  94. package/dist/components/registry/niko-table/ui/table.d.ts +13 -0
  95. package/dist/components/registry/niko-table/ui/table.js +74 -0
  96. package/dist/components/registry/sidebar-07/app-sidebar.d.ts +18 -0
  97. package/dist/components/registry/sidebar-07/app-sidebar.js +26 -0
  98. package/dist/components/registry/sidebar-07/nav-main.d.ts +15 -0
  99. package/dist/components/registry/sidebar-07/nav-main.js +25 -0
  100. package/dist/components/registry/sidebar-07/nav-projects.d.ts +9 -0
  101. package/dist/components/registry/sidebar-07/nav-projects.js +43 -0
  102. package/dist/components/registry/sidebar-07/nav-user.d.ts +9 -0
  103. package/dist/components/registry/sidebar-07/nav-user.js +70 -0
  104. package/dist/components/registry/sidebar-07/team-switcher.d.ts +10 -0
  105. package/dist/components/registry/sidebar-07/team-switcher.js +70 -0
  106. package/dist/components/siyahi/delta.d.ts +10 -0
  107. package/dist/components/siyahi/delta.js +27 -0
  108. package/dist/components/siyahi/local-time.d.ts +7 -0
  109. package/dist/components/siyahi/local-time.js +22 -0
  110. package/dist/components/siyahi/price-chart.d.ts +12 -0
  111. package/dist/components/siyahi/price-chart.js +80 -0
  112. package/dist/components/siyahi/siyahi-provider.d.ts +12 -0
  113. package/dist/components/siyahi/siyahi-provider.js +31 -0
  114. package/dist/components/siyahi/treemap.d.ts +14 -0
  115. package/dist/components/siyahi/treemap.js +58 -0
  116. package/dist/components/ui/alert.d.ts +12 -0
  117. package/dist/components/ui/alert.js +43 -0
  118. package/dist/components/ui/avatar.d.ts +13 -0
  119. package/dist/components/ui/avatar.js +50 -0
  120. package/dist/components/ui/badge.d.ts +9 -0
  121. package/dist/components/ui/badge.js +29 -0
  122. package/dist/components/ui/breadcrumb.d.ts +12 -0
  123. package/dist/components/ui/breadcrumb.js +72 -0
  124. package/dist/components/ui/button.d.ts +10 -0
  125. package/dist/components/ui/button.js +44 -0
  126. package/dist/components/ui/calendar.d.ts +12 -0
  127. package/dist/components/ui/calendar.js +107 -0
  128. package/dist/components/ui/card.d.ts +13 -0
  129. package/dist/components/ui/card.js +56 -0
  130. package/dist/components/ui/chart.d.ts +46 -0
  131. package/dist/components/ui/chart.js +149 -0
  132. package/dist/components/ui/collapsible.d.ts +7 -0
  133. package/dist/components/ui/collapsible.js +24 -0
  134. package/dist/components/ui/command.d.ts +21 -0
  135. package/dist/components/ui/command.js +88 -0
  136. package/dist/components/ui/context-menu.d.ts +31 -0
  137. package/dist/components/ui/context-menu.js +132 -0
  138. package/dist/components/ui/dialog.d.ts +19 -0
  139. package/dist/components/ui/dialog.js +91 -0
  140. package/dist/components/ui/dropdown-menu.d.ts +31 -0
  141. package/dist/components/ui/dropdown-menu.js +136 -0
  142. package/dist/components/ui/input-group.d.ts +20 -0
  143. package/dist/components/ui/input-group.js +79 -0
  144. package/dist/components/ui/input.d.ts +5 -0
  145. package/dist/components/ui/input.js +15 -0
  146. package/dist/components/ui/popover.d.ts +11 -0
  147. package/dist/components/ui/popover.js +55 -0
  148. package/dist/components/ui/progress.d.ts +9 -0
  149. package/dist/components/ui/progress.js +43 -0
  150. package/dist/components/ui/scroll-area.d.ts +7 -0
  151. package/dist/components/ui/scroll-area.js +36 -0
  152. package/dist/components/ui/select.d.ts +17 -0
  153. package/dist/components/ui/select.js +97 -0
  154. package/dist/components/ui/separator.d.ts +5 -0
  155. package/dist/components/ui/separator.js +15 -0
  156. package/dist/components/ui/sheet.d.ts +16 -0
  157. package/dist/components/ui/sheet.js +89 -0
  158. package/dist/components/ui/sidebar.d.ts +65 -0
  159. package/dist/components/ui/sidebar.js +385 -0
  160. package/dist/components/ui/skeleton.d.ts +4 -0
  161. package/dist/components/ui/skeleton.js +12 -0
  162. package/dist/components/ui/slider.d.ts +5 -0
  163. package/dist/components/ui/slider.js +33 -0
  164. package/dist/components/ui/table.d.ts +12 -0
  165. package/dist/components/ui/table.js +66 -0
  166. package/dist/components/ui/tabs.d.ts +12 -0
  167. package/dist/components/ui/tabs.js +45 -0
  168. package/dist/components/ui/textarea.d.ts +5 -0
  169. package/dist/components/ui/textarea.js +13 -0
  170. package/dist/components/ui/toggle-group.d.ts +13 -0
  171. package/dist/components/ui/toggle-group.js +52 -0
  172. package/dist/components/ui/toggle.d.ts +10 -0
  173. package/dist/components/ui/toggle.js +36 -0
  174. package/dist/components/ui/tooltip.d.ts +8 -0
  175. package/dist/components/ui/tooltip.js +41 -0
  176. package/dist/hooks/use-mobile.d.ts +3 -0
  177. package/dist/hooks/use-mobile.js +18 -0
  178. package/dist/index.d.ts +73 -0
  179. package/dist/index.js +71 -0
  180. package/dist/lib/format.d.ts +24 -0
  181. package/dist/lib/format.js +58 -0
  182. package/dist/lib/locale.d.ts +12 -0
  183. package/dist/lib/locale.js +8 -0
  184. package/dist/lib/recharts.d.ts +2 -0
  185. package/dist/lib/recharts.js +1 -0
  186. package/dist/lib/utils.d.ts +2 -0
  187. package/dist/lib/utils.js +1 -0
  188. package/package.json +78 -3
  189. package/registry.lock.json +413 -0
  190. package/report.css +129 -0
  191. package/styles/shadcn.css +643 -0
  192. 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