@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,370 @@
1
+ import { Skeleton } from "../../../ui/skeleton.js";
2
+ import { cn } from "../../../../lib/utils.js";
3
+ import { DataTableColumnHeaderRoot } from "../components/data-table-column-header.js";
4
+ import { flashCellKey, useDataTable } from "./data-table-context.js";
5
+ import { DataTableEmptyState } from "../components/data-table-empty-state.js";
6
+ import { resolveGroupedRowsSlot } from "../components/data-table-grouped-rows.js";
7
+ import { useResolvedRowContextMenuRenderer } from "../components/data-table-row-context-menu-slot.js";
8
+ import { DataTableRowContextMenu } from "../components/data-table-row-context-menu.js";
9
+ import { TableBody, TableCell, TableHead, TableHeader, TableRow } from "../ui/table.js";
10
+ import { resolveRowFromClick } from "../lib/row-click.js";
11
+ import { getCommonPinningStyles } from "../lib/styles.js";
12
+ import { GroupedBodyRow } from "./data-table-grouped-row.js";
13
+ import { isGlobalFilterActive } from "../lib/filter-functions.js";
14
+ import { DataTableColumnResizeHandle } from "../lib/column-resize-handle.js";
15
+ import { createScrollHandler } from "../lib/create-scroll-handler.js";
16
+ import { renderCellContent } from "../lib/render-cell-content.js";
17
+ import { resolveColumnWidth, resolveFlexColumnIds } from "../lib/flex-columns.js";
18
+ import React from "react";
19
+ import { Fragment as Fragment$1, jsx, jsxs } from "react/jsx-runtime";
20
+ import { flexRender } from "@tanstack/react-table";
21
+ //#region src/components/registry/niko-table/core/data-table-structure.tsx
22
+ const DataTableHeader = React.memo(function DataTableHeader({ className, sticky = true }) {
23
+ const { table, headerMinWidths, setColumnResizePreview } = useDataTable();
24
+ const resizing = table?.options.enableColumnResizing ?? false;
25
+ const headerGroups = table?.getHeaderGroups() ?? [];
26
+ if (headerGroups.length === 0) return null;
27
+ const flexColumnIds = resolveFlexColumnIds(table);
28
+ const columnSizing = table.state.columnSizing;
29
+ return /* @__PURE__ */ jsx(TableHeader, {
30
+ className: cn(sticky && "sticky top-0 z-30 bg-background", sticky && "after:absolute after:right-0 after:bottom-0 after:left-0 after:h-px after:bg-border", className),
31
+ children: headerGroups.map((headerGroup) => /* @__PURE__ */ jsx(TableRow, { children: headerGroup.headers.map((header) => {
32
+ const isFlex = flexColumnIds.has(header.column.id);
33
+ const headerStyle = {
34
+ width: resolveColumnWidth(header.column, {
35
+ resizing,
36
+ isFlex,
37
+ columnSizing,
38
+ headerMinWidths
39
+ }),
40
+ ...getCommonPinningStyles(header.column, true)
41
+ };
42
+ return /* @__PURE__ */ jsxs(TableHead, {
43
+ "data-col-id": header.column.id,
44
+ style: headerStyle,
45
+ className: cn(header.column.getIsPinned() && "bg-background", resizing && "relative overflow-hidden"),
46
+ children: [header.isPlaceholder ? null : /* @__PURE__ */ jsx(DataTableColumnHeaderRoot, {
47
+ column: header.column,
48
+ children: resizing && typeof header.column.columnDef.header === "string" ? /* @__PURE__ */ jsx("span", {
49
+ className: "inline-block max-w-full truncate",
50
+ children: header.column.columnDef.header
51
+ }) : flexRender(header.column.columnDef.header, header.getContext())
52
+ }), resizing && header.column.getCanResize() && /* @__PURE__ */ jsx(DataTableColumnResizeHandle, {
53
+ header,
54
+ isFlex,
55
+ setResizePreview: setColumnResizePreview
56
+ })]
57
+ }, header.id);
58
+ }) }, headerGroup.id))
59
+ });
60
+ });
61
+ DataTableHeader.displayName = "DataTableHeader";
62
+ /** Fade-pulse animation applied to a flashing cell (keyframe in the provider). */
63
+ const FLASH_ANIMATION = "niko-row-flash 1.2s ease-out";
64
+ const BodyRow = React.memo(function BodyRow({ row, displayIndex, expandColumnId, isClickable, isExpanded, isSelected, columnWidths, isRowFlashing, flashingCellKeys, renderRowContextMenu }) {
65
+ const expandCell = isExpanded && expandColumnId ? row.getAllCells().find((c) => c.column.id === expandColumnId) : void 0;
66
+ const visibleCells = row.getVisibleCells();
67
+ const rowElement = /* @__PURE__ */ jsx(TableRow, {
68
+ "data-row-index": displayIndex,
69
+ "data-row-id": row.id,
70
+ "data-row-type": row.original?.rowType,
71
+ "data-parity": displayIndex % 2 === 0 ? "even" : "odd",
72
+ "data-expanded": isExpanded ? "true" : void 0,
73
+ "data-state": isSelected ? "selected" : void 0,
74
+ className: cn(isClickable && "cursor-pointer", "group data-[context-menu-open]:bg-muted/50"),
75
+ children: visibleCells.map((cell) => {
76
+ const flashing = isRowFlashing || flashingCellKeys.has(flashCellKey(row.id, cell.column.id));
77
+ const cellStyle = {
78
+ width: columnWidths.get(cell.column.id),
79
+ ...getCommonPinningStyles(cell.column, false),
80
+ ...flashing ? { animation: FLASH_ANIMATION } : {}
81
+ };
82
+ return /* @__PURE__ */ jsx(TableCell, {
83
+ "data-flash": flashing ? "true" : void 0,
84
+ "data-col-id": cell.column.id,
85
+ style: cellStyle,
86
+ className: cn(!cell.getIsGrouped() && "truncate", cell.column.getIsPinned() && "bg-background group-hover:bg-muted/50 group-data-[context-menu-open]:bg-muted/50 group-data-[state=selected]:bg-muted"),
87
+ children: renderCellContent(cell)
88
+ }, cell.id);
89
+ })
90
+ });
91
+ const menuItems = renderRowContextMenu?.(row.original);
92
+ return /* @__PURE__ */ jsxs(Fragment$1, { children: [menuItems ? /* @__PURE__ */ jsx(DataTableRowContextMenu, {
93
+ row: row.original,
94
+ trigger: rowElement,
95
+ children: menuItems
96
+ }) : rowElement, expandCell && /* @__PURE__ */ jsx(TableRow, { children: /* @__PURE__ */ jsx(TableCell, {
97
+ colSpan: visibleCells.length,
98
+ className: "p-0",
99
+ children: expandCell.column.columnDef.meta?.expandedContent?.(row.original)
100
+ }) })] });
101
+ });
102
+ BodyRow.displayName = "BodyRow";
103
+ function DataTableBody({ children, className, onScroll, onScrolledTop, onScrolledBottom, scrollThreshold = 50, onRowClick, getRowMemoKey, renderRowContextMenu }) {
104
+ const { table, columns, isLoading, flashingRowIds, flashingCellKeys, registerRowScroller, headerMinWidths } = useDataTable();
105
+ const { rows } = table.getRowModel();
106
+ const containerRef = React.useRef(null);
107
+ React.useEffect(() => {
108
+ registerRowScroller((index, opts) => {
109
+ const row = (containerRef.current?.closest("[data-slot=\"table-container\"]"))?.querySelector(`[data-row-index="${index}"]`);
110
+ const block = opts?.align === void 0 || opts.align === "auto" ? "nearest" : opts.align;
111
+ row?.scrollIntoView({ block });
112
+ });
113
+ return () => registerRowScroller(null);
114
+ }, [registerRowScroller]);
115
+ React.useEffect(() => {
116
+ const container = containerRef.current?.closest("[data-slot=\"table-container\"]");
117
+ if (!container) return;
118
+ if (!onScroll && !onScrolledTop && !onScrolledBottom) return;
119
+ const handleScroll = createScrollHandler({
120
+ onScroll,
121
+ onScrolledTop,
122
+ onScrolledBottom,
123
+ scrollThreshold
124
+ });
125
+ container.addEventListener("scroll", handleScroll, { passive: true });
126
+ return () => container.removeEventListener("scroll", handleScroll);
127
+ }, [
128
+ onScroll,
129
+ onScrolledTop,
130
+ onScrolledBottom,
131
+ scrollThreshold
132
+ ]);
133
+ const handleBodyClick = React.useCallback((event) => {
134
+ if (!onRowClick) return;
135
+ const row = resolveRowFromClick(event.target, table);
136
+ if (!row) return;
137
+ onRowClick(row.original, event);
138
+ }, [onRowClick, table]);
139
+ const expandColumnId = React.useMemo(() => table.getAllColumns().find((col) => col.columnDef.meta?.expandedContent)?.id, [table, columns]);
140
+ const { columnVisibility, columnOrder, columnPinning, columnSizing } = table.state;
141
+ const resizing = table.options.enableColumnResizing ?? false;
142
+ const flexColumnIds = React.useMemo(() => resolveFlexColumnIds(table), [
143
+ table,
144
+ columnVisibility,
145
+ columnOrder,
146
+ columnPinning,
147
+ columnSizing,
148
+ resizing
149
+ ]);
150
+ const columnWidths = React.useMemo(() => {
151
+ const widths = /* @__PURE__ */ new Map();
152
+ for (const col of table.getVisibleLeafColumns()) widths.set(col.id, resolveColumnWidth(col, {
153
+ resizing,
154
+ isFlex: flexColumnIds.has(col.id),
155
+ columnSizing,
156
+ headerMinWidths
157
+ }));
158
+ return widths;
159
+ }, [
160
+ table,
161
+ flexColumnIds,
162
+ columnSizing,
163
+ columnVisibility,
164
+ columnOrder,
165
+ headerMinWidths,
166
+ resizing
167
+ ]);
168
+ const columnLayoutSignature = React.useMemo(() => table.getVisibleLeafColumns().map((c) => {
169
+ const pinned = c.getIsPinned();
170
+ const base = pinned ? `${c.id}:${pinned}` : c.id;
171
+ return resizing ? `${base}:${columnWidths.get(c.id) ?? "flex"}` : base;
172
+ }).join(","), [
173
+ table,
174
+ columns,
175
+ columnWidths,
176
+ resizing
177
+ ]);
178
+ const isClickable = !!onRowClick;
179
+ const resolvedRenderRowContextMenu = useResolvedRowContextMenuRenderer(renderRowContextMenu, children);
180
+ const groupedRowsSlot = React.useMemo(() => resolveGroupedRowsSlot(children), [children]);
181
+ const tableStyleSnapshotRef = React.useRef(null);
182
+ React.useLayoutEffect(() => {
183
+ const tableEl = containerRef.current?.closest("[data-slot=\"table\"]");
184
+ if (!tableEl) return;
185
+ const restore = () => {
186
+ const snap = tableStyleSnapshotRef.current;
187
+ if (!snap) return;
188
+ tableEl.style.tableLayout = snap.tableLayout;
189
+ tableEl.style.width = snap.width;
190
+ tableEl.style.minWidth = snap.minWidth;
191
+ tableStyleSnapshotRef.current = null;
192
+ };
193
+ if (!resizing) {
194
+ restore();
195
+ return;
196
+ }
197
+ if (!tableStyleSnapshotRef.current) tableStyleSnapshotRef.current = {
198
+ tableLayout: tableEl.style.tableLayout,
199
+ width: tableEl.style.width,
200
+ minWidth: tableEl.style.minWidth
201
+ };
202
+ const totalDesiredWidth = table.getVisibleLeafColumns().reduce((sum, col) => {
203
+ const width = columnWidths.get(col.id);
204
+ return sum + (typeof width === "number" ? width : col.getSize());
205
+ }, 0);
206
+ const hasFlex = flexColumnIds.size > 0;
207
+ tableEl.style.tableLayout = "fixed";
208
+ tableEl.style.width = hasFlex ? "100%" : `${totalDesiredWidth}px`;
209
+ tableEl.style.minWidth = `${totalDesiredWidth}px`;
210
+ return restore;
211
+ }, [
212
+ resizing,
213
+ table,
214
+ columnWidths,
215
+ flexColumnIds,
216
+ columnVisibility,
217
+ columnOrder,
218
+ columnPinning
219
+ ]);
220
+ return /* @__PURE__ */ jsxs(TableBody, {
221
+ ref: containerRef,
222
+ className,
223
+ onClick: onRowClick ? handleBodyClick : void 0,
224
+ children: [!isLoading && rows?.length ? rows.map((row, displayIndex) => groupedRowsSlot && row.getIsGrouped() ? /* @__PURE__ */ jsx(GroupedBodyRow, {
225
+ row,
226
+ displayIndex,
227
+ isExpanded: row.getIsExpanded(),
228
+ columnWidths,
229
+ columnLayoutSignature,
230
+ slot: groupedRowsSlot
231
+ }, row.id) : /* @__PURE__ */ jsx(BodyRow, {
232
+ row,
233
+ displayIndex,
234
+ expandColumnId,
235
+ isClickable,
236
+ isExpanded: row.getIsExpanded(),
237
+ isSelected: row.getIsSelected(),
238
+ columnWidths,
239
+ columnLayoutSignature,
240
+ rowMemoKey: getRowMemoKey ? getRowMemoKey(row.original) : "",
241
+ isRowFlashing: flashingRowIds.has(row.id),
242
+ flashingCellKeys,
243
+ renderRowContextMenu: resolvedRenderRowContextMenu
244
+ }, row.id)) : null, children]
245
+ });
246
+ }
247
+ DataTableBody.displayName = "DataTableBody";
248
+ /**
249
+ * Empty state component for data tables.
250
+ * Use composition pattern with DataTableEmpty* components for full customization.
251
+ *
252
+ * @example
253
+ * <DataTableEmptyBody>
254
+ * <DataTableEmptyIcon>
255
+ * <PackageOpen className="size-12" />
256
+ * </DataTableEmptyIcon>
257
+ * <DataTableEmptyMessage>
258
+ * <DataTableEmptyTitle>No products found</DataTableEmptyTitle>
259
+ * <DataTableEmptyDescription>
260
+ * Get started by adding your first product
261
+ * </DataTableEmptyDescription>
262
+ * </DataTableEmptyMessage>
263
+ * <DataTableEmptyFilteredMessage>
264
+ * No matches found
265
+ * </DataTableEmptyFilteredMessage>
266
+ * <DataTableEmptyActions>
267
+ * <Button onClick={handleAdd}>Add Product</Button>
268
+ * </DataTableEmptyActions>
269
+ * </DataTableEmptyBody>
270
+ */
271
+ function DataTableEmptyBody({ children, colSpan, className }) {
272
+ const { table, columns, isLoading } = useDataTable();
273
+ const tableState = table.state;
274
+ const isFiltered = React.useMemo(() => isGlobalFilterActive(tableState.globalFilter) || tableState.columnFilters && tableState.columnFilters.length > 0, [tableState.globalFilter, tableState.columnFilters]);
275
+ const rowCount = table.getRowModel().rows.length;
276
+ if (isLoading || rowCount > 0) return null;
277
+ const visibleCount = table.getVisibleLeafColumns().length;
278
+ return /* @__PURE__ */ jsx(TableRow, { children: /* @__PURE__ */ jsx(TableCell, {
279
+ colSpan: colSpan ?? (visibleCount || columns.length),
280
+ className,
281
+ children: /* @__PURE__ */ jsx(DataTableEmptyState, {
282
+ isFiltered,
283
+ children
284
+ })
285
+ }) });
286
+ }
287
+ DataTableEmptyBody.displayName = "DataTableEmptyBody";
288
+ function DataTableSkeleton({ children, colSpan, rows = 5, className, cellClassName, skeletonClassName }) {
289
+ const { table, columns, isLoading } = useDataTable();
290
+ if (!isLoading) return null;
291
+ const visibleColumns = table.getVisibleLeafColumns();
292
+ const numColumns = colSpan ?? (visibleColumns.length || columns.length);
293
+ if (children) return /* @__PURE__ */ jsx(TableRow, { children: /* @__PURE__ */ jsx(TableCell, {
294
+ colSpan: numColumns,
295
+ className: cn("h-24 text-center", className),
296
+ children
297
+ }) });
298
+ return /* @__PURE__ */ jsx(Fragment$1, { children: Array.from({ length: rows }).map((_, rowIndex) => /* @__PURE__ */ jsx(TableRow, { children: visibleColumns.map((column, colIndex) => {
299
+ const size = column.columnDef.size;
300
+ const cellStyle = size ? { width: `${size}px` } : void 0;
301
+ return /* @__PURE__ */ jsx(TableCell, {
302
+ className: cellClassName,
303
+ style: cellStyle,
304
+ children: /* @__PURE__ */ jsx(Skeleton, { className: cn("h-4 w-full", skeletonClassName) })
305
+ }, colIndex);
306
+ }) }, rowIndex)) });
307
+ }
308
+ DataTableSkeleton.displayName = "DataTableSkeleton";
309
+ function DataTableLoading({ children, colSpan, className }) {
310
+ const { table, columns, isLoading } = useDataTable();
311
+ if (!isLoading) return null;
312
+ const visibleCount = table.getVisibleLeafColumns().length;
313
+ return /* @__PURE__ */ jsx(TableRow, { children: /* @__PURE__ */ jsx(TableCell, {
314
+ colSpan: colSpan ?? (visibleCount || columns.length),
315
+ className: className ?? "h-24 text-center",
316
+ children: children ?? /* @__PURE__ */ jsxs("div", {
317
+ className: "flex items-center justify-center gap-2",
318
+ children: [/* @__PURE__ */ jsx("div", { className: "h-4 w-4 animate-spin rounded-full border-2 border-primary border-t-transparent" }), /* @__PURE__ */ jsx("span", {
319
+ className: "text-sm text-muted-foreground",
320
+ children: "Loading..."
321
+ })]
322
+ })
323
+ }) });
324
+ }
325
+ DataTableLoading.displayName = "DataTableLoading";
326
+ /**
327
+ * Composable "loading more" row for infinite-scroll tables. Renders
328
+ * a spinner + label row at the end of the body when `isFetching` is
329
+ * true, and nothing when false — designed to be dropped as a child
330
+ * of `DataTableBody` alongside `DataTableSkeleton` and
331
+ * `DataTableEmptyBody`.
332
+ *
333
+ * Mirror of `DataTableVirtualizedLoadingMore` for non-virtualized
334
+ * tables — same API, same styling, same self-gating behavior.
335
+ *
336
+ * @example
337
+ * const query = api.thing.list.useInfiniteQuery(...);
338
+ * <DataTableBody
339
+ * onScrolledBottom={() => {
340
+ * if (query.hasNextPage && !query.isFetchingNextPage) {
341
+ * void query.fetchNextPage();
342
+ * }
343
+ * }}
344
+ * >
345
+ * <DataTableSkeleton rows={5} />
346
+ * <DataTableEmptyBody>No results</DataTableEmptyBody>
347
+ * <DataTableLoadingMore isFetching={query.isFetchingNextPage}>
348
+ * Loading more things…
349
+ * </DataTableLoadingMore>
350
+ * </DataTableBody>
351
+ */
352
+ function DataTableLoadingMore({ isFetching, children, colSpan, className }) {
353
+ const { table, columns } = useDataTable();
354
+ if (!isFetching) return null;
355
+ const visibleCount = table.getVisibleLeafColumns().length;
356
+ return /* @__PURE__ */ jsx(TableRow, {
357
+ "data-slot": "datatable-loading-more-row",
358
+ children: /* @__PURE__ */ jsx(TableCell, {
359
+ colSpan: colSpan ?? (visibleCount || columns.length),
360
+ className: cn("text-center text-xs text-muted-foreground", className),
361
+ children: /* @__PURE__ */ jsxs("span", {
362
+ className: "inline-flex items-center justify-center gap-2 py-3",
363
+ children: [/* @__PURE__ */ jsx("span", { className: "inline-block size-3.5 animate-spin rounded-full border-2 border-muted-foreground/60 border-t-transparent" }), children ?? "Loading more…"]
364
+ })
365
+ })
366
+ });
367
+ }
368
+ DataTableLoadingMore.displayName = "DataTableLoadingMore";
369
+ //#endregion
370
+ export { DataTableBody, DataTableEmptyBody, DataTableHeader, DataTableLoading, DataTableLoadingMore, DataTableSkeleton };
@@ -0,0 +1,30 @@
1
+ import React from "react";
2
+ //#region src/components/registry/niko-table/core/data-table.d.ts
3
+ export interface DataTableContainerProps {
4
+ children: React.ReactNode;
5
+ /**
6
+ * Additional CSS classes for the container.
7
+ * Arbitrary height values (e.g., h-[600px], max-h-[400px]) are automatically extracted
8
+ * and applied as inline styles to ensure scroll event callbacks work reliably.
9
+ * For other height utilities, use the height/maxHeight props directly.
10
+ */
11
+ className?: string;
12
+ /**
13
+ * Sets the height of the table container.
14
+ * When provided, enables vertical scrolling and allows DataTableBody/DataTableVirtualizedBody
15
+ * to use onScroll, onScrolledTop, and onScrolledBottom callbacks.
16
+ * Takes precedence over height utilities in className.
17
+ */
18
+ height?: number | string;
19
+ /**
20
+ * Sets the maximum height of the table container.
21
+ * Defaults to the height value if not specified.
22
+ * Takes precedence over max-height utilities in className.
23
+ */
24
+ maxHeight?: number | string;
25
+ }
26
+ export declare function DataTable({ children, className, height, maxHeight }: DataTableContainerProps): React.JSX.Element;
27
+ export declare namespace DataTable {
28
+ var displayName: string;
29
+ }
30
+ //#endregion
@@ -0,0 +1,126 @@
1
+ "use client";
2
+ import { cn } from "../../../../lib/utils.js";
3
+ import { useColumnResizeInfo, useDataTable } from "./data-table-context.js";
4
+ import { TableComponent } from "../ui/table.js";
5
+ import React from "react";
6
+ import { jsx, jsxs } from "react/jsx-runtime";
7
+ //#region src/components/registry/niko-table/core/data-table.tsx
8
+ /**
9
+ * niko-table — created by Semir N. (Semkoo, https://github.com/Semkoo) with AI assistance.
10
+ *
11
+ * Before reporting anything: please check the changelog first.
12
+ * - In-repo: ./CHANGELOG.md
13
+ * - Docs site: https://niko-table.com/changelog
14
+ *
15
+ * Found a bug or have a fix? Open an issue or PR on GitHub so other
16
+ * users (and future LLMs reading this code) benefit:
17
+ * https://github.com/Semkoo/niko-table-registry
18
+ */
19
+ /**
20
+ * Extracts height from Tailwind arbitrary values (e.g., h-[600px], max-h-[400px]).
21
+ * Converts them to inline styles to ensure scroll events work reliably.
22
+ * For other height utilities, use the height/maxHeight props directly.
23
+ */
24
+ function parseHeightFromClassName(className) {
25
+ if (!className) return {
26
+ height: void 0,
27
+ maxHeight: void 0,
28
+ safeClassName: className
29
+ };
30
+ const classes = className.split(/\s+/);
31
+ let height;
32
+ let maxHeight;
33
+ const remainingClasses = [];
34
+ for (const cls of classes) {
35
+ const heightMatch = cls.match(/^h-\[([^\]]+)\]$/);
36
+ const maxHeightMatch = cls.match(/^max-h-\[([^\]]+)\]$/);
37
+ if (heightMatch) height = heightMatch[1];
38
+ else if (maxHeightMatch) maxHeight = maxHeightMatch[1];
39
+ else remainingClasses.push(cls);
40
+ }
41
+ return {
42
+ height,
43
+ maxHeight,
44
+ safeClassName: remainingClasses.join(" ")
45
+ };
46
+ }
47
+ /**
48
+ * DataTable container component that wraps the table and provides scrolling behavior.
49
+ *
50
+ * @example
51
+ * Without height - table grows with content, no scroll
52
+ * <DataTable>
53
+ * <DataTableHeader />
54
+ * <DataTableBody />
55
+ * </DataTable>
56
+ *
57
+ * @example
58
+ * With height prop - enables scrolling and scroll event callbacks
59
+ * <DataTable height={600}>
60
+ * <DataTableHeader />
61
+ * <DataTableBody
62
+ * onScroll={(e) => console.log(`Scrolled ${e.percentage}%`)}
63
+ * onScrolledBottom={() => console.log('Load more data')}
64
+ * />
65
+ * </DataTable>
66
+ *
67
+ * @example
68
+ * With arbitrary height in className - automatically extracted and applied as inline style
69
+ * <DataTable className="h-[600px]">
70
+ * <DataTableBody onScroll={...} />
71
+ * </DataTable>
72
+ *
73
+ * @example
74
+ * Prefer using height prop for better type safety and clarity
75
+ * <DataTable height="600px" className="rounded-lg">
76
+ * <DataTableBody onScroll={...} />
77
+ * </DataTable>
78
+ */
79
+ /**
80
+ * A single vertical guide line that follows the cursor while a column is being
81
+ * resized. Resizing runs in `onEnd` mode, so the columns themselves don't move
82
+ * until the drag ends (that's what keeps heavy tables smooth); this line gives
83
+ * the live "where the edge will land" feedback in the meantime.
84
+ *
85
+ * It subscribes to the dedicated resize-info context, so it — and nothing else
86
+ * in the table — re-renders per pointer move. Positioned in the scroll
87
+ * container's content space so it tracks correctly through horizontal scroll.
88
+ */
89
+ function ColumnResizePreviewLine() {
90
+ const { resizingColumnId, deltaOffset } = useColumnResizeInfo();
91
+ const { scrollContainer } = useDataTable();
92
+ if (!resizingColumnId || !scrollContainer) return null;
93
+ const escapedId = typeof CSS !== "undefined" && CSS.escape ? CSS.escape(resizingColumnId) : resizingColumnId;
94
+ const cell = scrollContainer.querySelector(`thead [data-col-id="${escapedId}"]`);
95
+ if (!cell) return null;
96
+ const containerRect = scrollContainer.getBoundingClientRect();
97
+ const left = cell.getBoundingClientRect().right - containerRect.left + scrollContainer.scrollLeft + deltaOffset;
98
+ return /* @__PURE__ */ jsx("div", {
99
+ "aria-hidden": true,
100
+ "data-slot": "column-resize-preview",
101
+ className: "bg-primary pointer-events-none absolute top-0 z-40 w-px",
102
+ style: {
103
+ left,
104
+ height: scrollContainer.scrollHeight
105
+ }
106
+ });
107
+ }
108
+ function DataTable({ children, className, height, maxHeight }) {
109
+ const parsed = React.useMemo(() => parseHeightFromClassName(className), [className]);
110
+ const finalHeight = height ?? parsed.height;
111
+ const finalMaxHeight = maxHeight ?? parsed.maxHeight ?? finalHeight;
112
+ const { registerScrollContainer } = useDataTable();
113
+ return /* @__PURE__ */ jsxs("div", {
114
+ ref: registerScrollContainer,
115
+ "data-slot": "table-container",
116
+ className: cn("relative w-full overflow-auto rounded-lg border", "[&::-webkit-scrollbar]:h-2.5 [&::-webkit-scrollbar]:w-2.5", "[&::-webkit-scrollbar-track]:bg-transparent", "[&::-webkit-scrollbar-thumb]:rounded-full [&::-webkit-scrollbar-thumb]:bg-border/40", "hover:[&::-webkit-scrollbar-thumb]:bg-border", "[&::-webkit-scrollbar-thumb:hover]:bg-border/80!", "scrollbar-thin scrollbar-thumb-border/40 scrollbar-track-transparent", "hover:scrollbar-thumb-border", parsed.safeClassName),
117
+ style: {
118
+ height: finalHeight,
119
+ maxHeight: finalMaxHeight
120
+ },
121
+ children: [/* @__PURE__ */ jsx(TableComponent, { children }), /* @__PURE__ */ jsx(ColumnResizePreviewLine, {})]
122
+ });
123
+ }
124
+ DataTable.displayName = "DataTable";
125
+ //#endregion
126
+ export { DataTable };
@@ -0,0 +1,49 @@
1
+ import React from "react";
2
+ //#region src/components/registry/niko-table/filters/table-column-actions.d.ts
3
+ export interface TableColumnActionsProps {
4
+ children: React.ReactNode;
5
+ className?: string;
6
+ /**
7
+ * Optional label shown at the top of the dropdown.
8
+ * @default "Column Actions"
9
+ */
10
+ label?: string;
11
+ /**
12
+ * Whether to show a visual indicator when actions are active.
13
+ */
14
+ isActive?: boolean;
15
+ /**
16
+ * Custom trigger element. If not provided, uses a MoreVertical icon button.
17
+ */
18
+ trigger?: React.ReactElement;
19
+ /**
20
+ * Alignment of the dropdown content.
21
+ * @default "end"
22
+ */
23
+ align?: "start" | "center" | "end";
24
+ }
25
+ /**
26
+ * A simple dropdown container for composing column actions.
27
+ *
28
+ * Use with `*Options` components to compose actions in a single dropdown:
29
+ *
30
+ * @example
31
+ * ```tsx
32
+ * <TableColumnActions>
33
+ * <TableColumnSortOptions />
34
+ * <TableColumnPinOptions />
35
+ * <TableColumnHideOptions />
36
+ * </TableColumnActions>
37
+ * ```
38
+ *
39
+ * For standalone dropdowns, use the `*Menu` variants instead:
40
+ * ```tsx
41
+ * <TableColumnSortMenu />
42
+ * <TableColumnPin />
43
+ * ```
44
+ */
45
+ export declare function TableColumnActions({ children, className, label, isActive, trigger, align }: TableColumnActionsProps): React.JSX.Element;
46
+ export declare namespace TableColumnActions {
47
+ var displayName: string;
48
+ }
49
+ //#endregion
@@ -0,0 +1,48 @@
1
+ import { Button } from "../../../ui/button.js";
2
+ import { DropdownMenu, DropdownMenuContent, DropdownMenuLabel, DropdownMenuTrigger } from "../../../ui/dropdown-menu.js";
3
+ import { cn } from "../../../../lib/utils.js";
4
+ import "react";
5
+ import { jsx, jsxs } from "react/jsx-runtime";
6
+ import { MoreVertical } from "lucide-react";
7
+ //#region src/components/registry/niko-table/filters/table-column-actions.tsx
8
+ /**
9
+ * A simple dropdown container for composing column actions.
10
+ *
11
+ * Use with `*Options` components to compose actions in a single dropdown:
12
+ *
13
+ * @example
14
+ * ```tsx
15
+ * <TableColumnActions>
16
+ * <TableColumnSortOptions />
17
+ * <TableColumnPinOptions />
18
+ * <TableColumnHideOptions />
19
+ * </TableColumnActions>
20
+ * ```
21
+ *
22
+ * For standalone dropdowns, use the `*Menu` variants instead:
23
+ * ```tsx
24
+ * <TableColumnSortMenu />
25
+ * <TableColumnPin />
26
+ * ```
27
+ */
28
+ function TableColumnActions({ children, className, label = "Column Actions", isActive = false, trigger, align = "end" }) {
29
+ return /* @__PURE__ */ jsxs(DropdownMenu, { children: [/* @__PURE__ */ jsx(DropdownMenuTrigger, { render: trigger ?? /* @__PURE__ */ jsxs(Button, {
30
+ variant: "ghost",
31
+ size: "icon",
32
+ className: cn("size-7 transition-opacity group-hover:opacity-100 dark:text-muted-foreground", isActive ? "text-primary opacity-100" : "opacity-0", className),
33
+ children: [/* @__PURE__ */ jsx(MoreVertical, { className: "size-4" }), /* @__PURE__ */ jsx("span", {
34
+ className: "sr-only",
35
+ children: label
36
+ })]
37
+ }) }), /* @__PURE__ */ jsxs(DropdownMenuContent, {
38
+ align,
39
+ className: "w-48",
40
+ children: [/* @__PURE__ */ jsx(DropdownMenuLabel, {
41
+ className: "text-xs font-normal text-muted-foreground",
42
+ children: label
43
+ }), children]
44
+ })] });
45
+ }
46
+ TableColumnActions.displayName = "TableColumnActions";
47
+ //#endregion
48
+ export { TableColumnActions };
@@ -0,0 +1,51 @@
1
+ import { DataTableColumn, DataTableInstance } from "../types/index.js";
2
+ import { SortIconVariant } from "../config/data-table.js";
3
+ import React from "react";
4
+ import { RowData } from "@tanstack/react-table";
5
+ //#region src/components/registry/niko-table/filters/table-column-sort.d.ts
6
+ /**
7
+ * Sort options menu items for composition inside TableColumnActions.
8
+ *
9
+ * @example
10
+ * ```tsx
11
+ * <TableColumnActions>
12
+ * <TableColumnSortOptions column={column} />
13
+ * </TableColumnActions>
14
+ * ```
15
+ */
16
+ export declare function TableColumnSortOptions<TData extends RowData, TValue>({ column, table: propTable, variant: propVariant, withSeparator }: {
17
+ column: DataTableColumn<TData, TValue>;
18
+ table?: DataTableInstance<TData>;
19
+ variant?: SortIconVariant;
20
+ /** Whether to render a separator before the options. @default true */
21
+ withSeparator?: boolean;
22
+ }): React.JSX.Element;
23
+ export declare namespace TableColumnSortOptions {
24
+ var displayName: string;
25
+ }
26
+ /**
27
+ * Standalone dropdown menu for sorting.
28
+ *
29
+ * For composition inside TableColumnActions, use TableColumnSortOptions instead.
30
+ *
31
+ * @example
32
+ * ```tsx
33
+ * // Standalone
34
+ * <TableColumnSortMenu column={column} table={table} />
35
+ *
36
+ * // Composed
37
+ * <TableColumnActions>
38
+ * <TableColumnSortOptions column={column} />
39
+ * </TableColumnActions>
40
+ * ```
41
+ */
42
+ export declare function TableColumnSortMenu<TData extends RowData, TValue>({ column, table: propTable, variant: propVariant, className }: {
43
+ column: DataTableColumn<TData, TValue>;
44
+ table?: DataTableInstance<TData>;
45
+ variant?: SortIconVariant;
46
+ className?: string;
47
+ }): React.JSX.Element | null;
48
+ export declare namespace TableColumnSortMenu {
49
+ var displayName: string;
50
+ }
51
+ //#endregion