@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,184 @@
1
+ "use client";
2
+ import { Button } from "../../../ui/button.js";
3
+ import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuLabel, DropdownMenuSeparator, DropdownMenuTrigger } from "../../../ui/dropdown-menu.js";
4
+ import { Tooltip, TooltipContent, TooltipTrigger } from "../../../ui/tooltip.js";
5
+ import { cn } from "../../../../lib/utils.js";
6
+ import { FILTER_VARIANTS } from "../lib/constants.js";
7
+ import { useDataTable } from "../core/data-table-context.js";
8
+ import { SORT_ICONS, SORT_LABELS } from "../config/data-table.js";
9
+ import React from "react";
10
+ import { Fragment as Fragment$1, jsx, jsxs } from "react/jsx-runtime";
11
+ import { Check, CircleHelp } from "lucide-react";
12
+ //#region src/components/registry/niko-table/filters/table-column-sort.tsx
13
+ /**
14
+ * niko-table — created by Semir N. (Semkoo, https://github.com/Semkoo) with AI assistance.
15
+ *
16
+ * Before reporting anything: please check the changelog first.
17
+ * - In-repo: ./CHANGELOG.md
18
+ * - Docs site: https://niko-table.com/changelog
19
+ *
20
+ * Found a bug or have a fix? Open an issue or PR on GitHub so other
21
+ * users (and future LLMs reading this code) benefit:
22
+ * https://github.com/Semkoo/niko-table-registry
23
+ */
24
+ /**
25
+ * Sort options menu items for composition inside TableColumnActions.
26
+ *
27
+ * @example
28
+ * ```tsx
29
+ * <TableColumnActions>
30
+ * <TableColumnSortOptions column={column} />
31
+ * </TableColumnActions>
32
+ * ```
33
+ */
34
+ function TableColumnSortOptions({ column, table: propTable, variant: propVariant, withSeparator = true }) {
35
+ const context = useDataTable();
36
+ const table = propTable || context.table;
37
+ const sortState = column.getIsSorted();
38
+ const variant = propVariant || column.columnDef.meta?.variant || FILTER_VARIANTS.TEXT;
39
+ const icons = SORT_ICONS[variant] || SORT_ICONS[FILTER_VARIANTS.TEXT];
40
+ const labels = SORT_LABELS[variant] || SORT_LABELS[FILTER_VARIANTS.TEXT];
41
+ const sortIndex = column.getSortIndex();
42
+ const showSortBadge = table && table.state.sorting.length > 1 && sortIndex !== -1;
43
+ /**
44
+ * Use a ref for immediate synchronous access to shift key state.
45
+ * React state updates are batched and async, which can cause race conditions
46
+ * when the dropdown closes - the keyup event might reset state before
47
+ * handleSort reads it. A ref provides synchronous access.
48
+ */
49
+ const isShiftPressedRef = React.useRef(false);
50
+ const [isShiftPressed, setIsShiftPressed] = React.useState(false);
51
+ React.useEffect(() => {
52
+ const handleKey = (e) => {
53
+ if (e.key === "Shift") {
54
+ const isDown = e.type === "keydown";
55
+ isShiftPressedRef.current = isDown;
56
+ setIsShiftPressed(isDown);
57
+ }
58
+ };
59
+ window.addEventListener("keydown", handleKey, { capture: true });
60
+ window.addEventListener("keyup", handleKey, { capture: true });
61
+ return () => {
62
+ window.removeEventListener("keydown", handleKey, { capture: true });
63
+ window.removeEventListener("keyup", handleKey, { capture: true });
64
+ };
65
+ }, []);
66
+ const handleSort = (direction, e) => {
67
+ const isMulti = isShiftPressedRef.current || isShiftPressed || e.shiftKey;
68
+ if (direction === false) column.clearSorting();
69
+ else {
70
+ const isDesc = direction === "desc";
71
+ const canMultiSort = column.getCanMultiSort();
72
+ /**
73
+ * @see https://tanstack.com/table/v8/docs/guide/sorting#multi-sorting
74
+ * When using toggleSorting explicitly, we must manually pass the multi-sort flag.
75
+ */
76
+ column.toggleSorting(isDesc, canMultiSort ? isMulti : false);
77
+ }
78
+ };
79
+ return /* @__PURE__ */ jsxs(Fragment$1, { children: [
80
+ withSeparator && /* @__PURE__ */ jsx(DropdownMenuSeparator, {}),
81
+ /* @__PURE__ */ jsxs(DropdownMenuLabel, {
82
+ className: "flex items-center justify-between text-xs font-normal text-muted-foreground",
83
+ children: [/* @__PURE__ */ jsxs("div", {
84
+ className: "flex items-center gap-2",
85
+ children: [/* @__PURE__ */ jsx("span", { children: "Column Sort" }), showSortBadge && /* @__PURE__ */ jsxs(Tooltip, { children: [/* @__PURE__ */ jsx(TooltipTrigger, {
86
+ render: /* @__PURE__ */ jsx("span", { className: "flex size-4 cursor-help items-center justify-center rounded-full bg-primary text-[10px] font-medium text-primary-foreground" }),
87
+ children: sortIndex + 1
88
+ }), /* @__PURE__ */ jsx(TooltipContent, {
89
+ side: "right",
90
+ children: "Sort priority (order in which columns are sorted)"
91
+ })] })]
92
+ }), /* @__PURE__ */ jsxs(Tooltip, { children: [/* @__PURE__ */ jsx(TooltipTrigger, { render: /* @__PURE__ */ jsx(CircleHelp, { className: "size-3.5 cursor-help" }) }), /* @__PURE__ */ jsx(TooltipContent, {
93
+ side: "right",
94
+ children: "TIP: Hold 'shift' key to enable multi sort"
95
+ })] })]
96
+ }),
97
+ /* @__PURE__ */ jsxs(DropdownMenuItem, {
98
+ onClick: (e) => handleSort("asc", e),
99
+ className: cn("flex items-center", sortState === "asc" && "bg-accent text-accent-foreground"),
100
+ children: [
101
+ /* @__PURE__ */ jsx(icons.asc, { className: "mr-2 size-4 text-muted-foreground/70" }),
102
+ /* @__PURE__ */ jsx("span", {
103
+ className: "flex-1",
104
+ children: labels.asc
105
+ }),
106
+ sortState === "asc" && /* @__PURE__ */ jsx(Check, { className: "ml-2 size-4" })
107
+ ]
108
+ }),
109
+ /* @__PURE__ */ jsxs(DropdownMenuItem, {
110
+ onClick: (e) => handleSort("desc", e),
111
+ className: cn("flex items-center", sortState === "desc" && "bg-accent text-accent-foreground"),
112
+ children: [
113
+ /* @__PURE__ */ jsx(icons.desc, { className: "mr-2 size-4 text-muted-foreground/70" }),
114
+ /* @__PURE__ */ jsx("span", {
115
+ className: "flex-1",
116
+ children: labels.desc
117
+ }),
118
+ sortState === "desc" && /* @__PURE__ */ jsx(Check, { className: "ml-2 size-4" })
119
+ ]
120
+ }),
121
+ sortState && /* @__PURE__ */ jsxs(DropdownMenuItem, {
122
+ onClick: () => column.clearSorting(),
123
+ children: [/* @__PURE__ */ jsx(icons.unsorted, { className: "mr-2 size-4 text-muted-foreground/70" }), "Clear Sort"]
124
+ })
125
+ ] });
126
+ }
127
+ /**
128
+ * Standalone dropdown menu for sorting.
129
+ *
130
+ * For composition inside TableColumnActions, use TableColumnSortOptions instead.
131
+ *
132
+ * @example
133
+ * ```tsx
134
+ * // Standalone
135
+ * <TableColumnSortMenu column={column} table={table} />
136
+ *
137
+ * // Composed
138
+ * <TableColumnActions>
139
+ * <TableColumnSortOptions column={column} />
140
+ * </TableColumnActions>
141
+ * ```
142
+ */
143
+ function TableColumnSortMenu({ column, table: propTable, variant: propVariant, className }) {
144
+ const context = useDataTable();
145
+ const table = propTable || context.table;
146
+ const canSort = column.getCanSort();
147
+ const sortState = column.getIsSorted();
148
+ const variant = propVariant || column.columnDef.meta?.variant || FILTER_VARIANTS.TEXT;
149
+ const icons = SORT_ICONS[variant] || SORT_ICONS[FILTER_VARIANTS.TEXT];
150
+ if (!canSort) return null;
151
+ const SortIcon = sortState === "asc" ? icons.asc : sortState === "desc" ? icons.desc : icons.unsorted;
152
+ const sortIndex = column.getSortIndex();
153
+ const showSortBadge = table && table.state.sorting.length > 1 && sortIndex !== -1;
154
+ return /* @__PURE__ */ jsxs(DropdownMenu, { children: [/* @__PURE__ */ jsxs(DropdownMenuTrigger, {
155
+ render: /* @__PURE__ */ jsx(Button, {
156
+ variant: "ghost",
157
+ size: "icon",
158
+ className: cn("size-7 transition-opacity dark:text-muted-foreground", sortState && "text-primary", className)
159
+ }),
160
+ children: [/* @__PURE__ */ jsxs("div", {
161
+ className: "relative flex items-center justify-center",
162
+ children: [/* @__PURE__ */ jsx(SortIcon, { className: "size-4" }), showSortBadge && /* @__PURE__ */ jsx("span", {
163
+ className: "absolute -top-1 -right-2 flex size-3 items-center justify-center rounded-full bg-primary text-[9px] text-primary-foreground",
164
+ children: sortIndex + 1
165
+ })]
166
+ }), /* @__PURE__ */ jsx("span", {
167
+ className: "sr-only",
168
+ children: "Sort column"
169
+ })]
170
+ }), /* @__PURE__ */ jsx(DropdownMenuContent, {
171
+ align: "end",
172
+ className: "w-48",
173
+ children: /* @__PURE__ */ jsx(TableColumnSortOptions, {
174
+ column,
175
+ table,
176
+ variant,
177
+ withSeparator: false
178
+ })
179
+ })] });
180
+ }
181
+ TableColumnSortOptions.displayName = "TableColumnSortOptions";
182
+ TableColumnSortMenu.displayName = "TableColumnSortMenu";
183
+ //#endregion
184
+ export { TableColumnSortMenu, TableColumnSortOptions };
@@ -0,0 +1,17 @@
1
+ import { DataTableColumn } from "../types/index.js";
2
+ import React from "react";
3
+ import { RowData } from "@tanstack/react-table";
4
+ //#region src/components/registry/niko-table/filters/table-column-title.d.ts
5
+ /**
6
+ * Renders the column title.
7
+ */
8
+ export declare function TableColumnTitle<TData extends RowData, TValue>({ column, title, className, children }: {
9
+ column: DataTableColumn<TData, TValue>;
10
+ title?: string;
11
+ className?: string;
12
+ children?: React.ReactNode;
13
+ }): React.JSX.Element;
14
+ export declare namespace TableColumnTitle {
15
+ var displayName: string;
16
+ }
17
+ //#endregion
@@ -0,0 +1,20 @@
1
+ "use client";
2
+ import { cn } from "../../../../lib/utils.js";
3
+ import { useDerivedColumnTitle } from "../hooks/use-derived-column-title.js";
4
+ import "react";
5
+ import { jsx } from "react/jsx-runtime";
6
+ //#region src/components/registry/niko-table/filters/table-column-title.tsx
7
+ /**
8
+ * Renders the column title.
9
+ */
10
+ function TableColumnTitle({ column, title, className, children }) {
11
+ const derivedTitle = useDerivedColumnTitle(column, column.id, title);
12
+ return /* @__PURE__ */ jsx("div", {
13
+ "data-slot": "column-title",
14
+ className: cn("min-w-0 truncate py-0.5 text-sm font-semibold transition-colors", className),
15
+ children: children ?? derivedTitle
16
+ });
17
+ }
18
+ TableColumnTitle.displayName = "TableColumnTitle";
19
+ //#endregion
20
+ export { TableColumnTitle };
@@ -0,0 +1,40 @@
1
+ import { DataTableColumn, Option } from "../types/index.js";
2
+ import * as React$1 from "react";
3
+ import { RowData } from "@tanstack/react-table";
4
+ //#region src/components/registry/niko-table/filters/table-faceted-filter.d.ts
5
+ export interface TableFacetedFilterProps<TData extends RowData, TValue> {
6
+ column?: DataTableColumn<TData, TValue>;
7
+ title?: string;
8
+ options: Option[];
9
+ multiple?: boolean;
10
+ /**
11
+ * Callback fired when filter value changes
12
+ * Useful for server-side filtering or external state management
13
+ */
14
+ onValueChange?: (value: string[] | undefined) => void;
15
+ /**
16
+ * Optional custom trigger element
17
+ */
18
+ trigger?: React$1.ReactElement;
19
+ }
20
+ export declare function useTableFacetedFilter<TData extends RowData, TValue = unknown>({ column, onValueChange, multiple }: {
21
+ column?: DataTableColumn<TData, TValue>;
22
+ onValueChange?: (value: string[] | undefined) => void;
23
+ multiple?: boolean;
24
+ }): {
25
+ selectedValues: Set<any>;
26
+ onItemSelect: (option: Option, isSelected: boolean) => void;
27
+ onReset: (event?: React$1.MouseEvent) => void;
28
+ };
29
+ export declare function TableFacetedFilter<TData extends RowData, TValue>({ column, title, options, multiple, onValueChange, trigger }: TableFacetedFilterProps<TData, TValue>): React$1.JSX.Element;
30
+ export declare namespace TableFacetedFilter {
31
+ var displayName: string;
32
+ }
33
+ export declare function TableFacetedFilterContent({ title, options, selectedValues, onItemSelect, onReset }: {
34
+ title?: string;
35
+ options: Option[];
36
+ selectedValues: Set<string>;
37
+ onItemSelect: (option: Option, isSelected: boolean) => void;
38
+ onReset: (event?: React$1.MouseEvent) => void;
39
+ }): React$1.JSX.Element;
40
+ //#endregion
@@ -0,0 +1,210 @@
1
+ "use client";
2
+ import { Badge } from "../../../ui/badge.js";
3
+ import { Button } from "../../../ui/button.js";
4
+ import { Command, CommandEmpty, CommandGroup, CommandInput, CommandItem, CommandList, CommandSeparator } from "../../../ui/command.js";
5
+ import { Popover, PopoverContent, PopoverTrigger } from "../../../ui/popover.js";
6
+ import { Separator } from "../../../ui/separator.js";
7
+ import { cn } from "../../../../lib/utils.js";
8
+ import { FILTER_OPERATORS, FILTER_VARIANTS, JOIN_OPERATORS } from "../lib/constants.js";
9
+ import * as React$1 from "react";
10
+ import { Fragment as Fragment$1, jsx, jsxs } from "react/jsx-runtime";
11
+ import { Check, PlusCircle, XCircle } from "lucide-react";
12
+ //#region src/components/registry/niko-table/filters/table-faceted-filter.tsx
13
+ /**
14
+ * niko-table — created by Semir N. (Semkoo, https://github.com/Semkoo) with AI assistance.
15
+ *
16
+ * Before reporting anything: please check the changelog first.
17
+ * - In-repo: ./CHANGELOG.md
18
+ * - Docs site: https://niko-table.com/changelog
19
+ *
20
+ * Found a bug or have a fix? Open an issue or PR on GitHub so other
21
+ * users (and future LLMs reading this code) benefit:
22
+ * https://github.com/Semkoo/niko-table-registry
23
+ */
24
+ /**
25
+ * Table faceted filter component
26
+ * @description A faceted filter component for DataTable that allows users to filter data based on multiple selectable options. It supports both single and multiple selection modes.
27
+ */
28
+ function useTableFacetedFilter({ column, onValueChange, multiple }) {
29
+ const columnFilterValue = column?.getFilterValue();
30
+ const selectedValues = React$1.useMemo(() => {
31
+ if (columnFilterValue && typeof columnFilterValue === "object" && !Array.isArray(columnFilterValue) && "value" in columnFilterValue) {
32
+ const filterValue = columnFilterValue.value;
33
+ return new Set(Array.isArray(filterValue) ? filterValue : [String(filterValue)]);
34
+ }
35
+ return new Set(Array.isArray(columnFilterValue) ? columnFilterValue : []);
36
+ }, [columnFilterValue]);
37
+ return {
38
+ selectedValues,
39
+ onItemSelect: React$1.useCallback((option, isSelected) => {
40
+ if (!column) return;
41
+ if (multiple) {
42
+ const newSelectedValues = new Set(selectedValues);
43
+ if (isSelected) newSelectedValues.delete(option.value);
44
+ else newSelectedValues.add(option.value);
45
+ const filterValues = Array.from(newSelectedValues);
46
+ if (filterValues.length === 0) {
47
+ column.setFilterValue(void 0);
48
+ onValueChange?.(void 0);
49
+ } else {
50
+ const extendedFilter = {
51
+ id: column.id,
52
+ value: filterValues,
53
+ variant: FILTER_VARIANTS.MULTI_SELECT,
54
+ operator: FILTER_OPERATORS.IN,
55
+ filterId: `faceted-${column.id}`,
56
+ joinOperator: JOIN_OPERATORS.AND
57
+ };
58
+ column.setFilterValue(extendedFilter);
59
+ onValueChange?.(filterValues);
60
+ }
61
+ } else if (isSelected) {
62
+ column.setFilterValue(void 0);
63
+ onValueChange?.(void 0);
64
+ } else {
65
+ const extendedFilter = {
66
+ id: column.id,
67
+ value: option.value,
68
+ variant: FILTER_VARIANTS.SELECT,
69
+ operator: FILTER_OPERATORS.EQ,
70
+ filterId: `faceted-${column.id}`,
71
+ joinOperator: JOIN_OPERATORS.AND
72
+ };
73
+ column.setFilterValue(extendedFilter);
74
+ onValueChange?.([option.value]);
75
+ }
76
+ }, [
77
+ column,
78
+ multiple,
79
+ selectedValues,
80
+ onValueChange
81
+ ]),
82
+ onReset: React$1.useCallback((event) => {
83
+ event?.stopPropagation();
84
+ column?.setFilterValue(void 0);
85
+ onValueChange?.(void 0);
86
+ }, [column, onValueChange])
87
+ };
88
+ }
89
+ function TableFacetedFilter({ column, title, options = [], multiple, onValueChange, trigger }) {
90
+ const [open, setOpen] = React$1.useState(false);
91
+ const { selectedValues, onItemSelect, onReset } = useTableFacetedFilter({
92
+ column,
93
+ onValueChange,
94
+ multiple
95
+ });
96
+ const handleItemSelect = React$1.useCallback((option, isSelected) => {
97
+ onItemSelect(option, isSelected);
98
+ if (!multiple) setOpen(false);
99
+ }, [
100
+ onItemSelect,
101
+ multiple,
102
+ setOpen
103
+ ]);
104
+ return /* @__PURE__ */ jsxs(Popover, {
105
+ open,
106
+ onOpenChange: setOpen,
107
+ children: [/* @__PURE__ */ jsx(PopoverTrigger, { render: trigger || /* @__PURE__ */ jsxs(Button, {
108
+ variant: "outline",
109
+ size: "sm",
110
+ className: "h-8 border-dashed",
111
+ children: [
112
+ selectedValues?.size > 0 ? /* @__PURE__ */ jsx("div", {
113
+ role: "button",
114
+ "aria-label": `Clear ${title} filter`,
115
+ tabIndex: 0,
116
+ onClick: onReset,
117
+ onKeyDown: (e) => {
118
+ if (e.key === "Enter" || e.key === " ") {
119
+ e.preventDefault();
120
+ onReset(e);
121
+ }
122
+ },
123
+ className: "rounded-sm opacity-70 transition-opacity hover:opacity-100 focus-visible:ring-1 focus-visible:ring-ring focus-visible:outline-none",
124
+ children: /* @__PURE__ */ jsx(XCircle, { className: "size-4" })
125
+ }) : /* @__PURE__ */ jsx(PlusCircle, { className: "size-4" }),
126
+ title,
127
+ selectedValues?.size > 0 && /* @__PURE__ */ jsxs(Fragment$1, { children: [
128
+ /* @__PURE__ */ jsx(Separator, {
129
+ orientation: "vertical",
130
+ className: "mx-2 h-4"
131
+ }),
132
+ /* @__PURE__ */ jsx(Badge, {
133
+ variant: "secondary",
134
+ className: "rounded-sm px-1 font-normal lg:hidden",
135
+ children: selectedValues.size
136
+ }),
137
+ /* @__PURE__ */ jsx("div", {
138
+ className: "hidden items-center gap-1 lg:flex",
139
+ children: selectedValues.size > 2 ? /* @__PURE__ */ jsxs(Badge, {
140
+ variant: "secondary",
141
+ className: "rounded-sm px-1 font-normal",
142
+ children: [selectedValues.size, " selected"]
143
+ }) : options.filter((option) => selectedValues.has(option.value)).map((option) => /* @__PURE__ */ jsx(Badge, {
144
+ variant: "secondary",
145
+ className: "rounded-sm px-1 font-normal",
146
+ children: option.label
147
+ }, option.value))
148
+ })
149
+ ] })
150
+ ]
151
+ }) }), /* @__PURE__ */ jsx(PopoverContent, {
152
+ className: "w-52 p-0",
153
+ align: "start",
154
+ children: /* @__PURE__ */ jsx(TableFacetedFilterContent, {
155
+ title,
156
+ options,
157
+ selectedValues,
158
+ onItemSelect: handleItemSelect,
159
+ onReset
160
+ })
161
+ })]
162
+ });
163
+ }
164
+ function TableFacetedFilterContent({ title, options, selectedValues, onItemSelect, onReset }) {
165
+ return /* @__PURE__ */ jsxs(Command, { children: [/* @__PURE__ */ jsx(CommandInput, {
166
+ placeholder: title,
167
+ className: "pl-2"
168
+ }), /* @__PURE__ */ jsxs(CommandList, {
169
+ className: "max-h-full",
170
+ children: [
171
+ /* @__PURE__ */ jsx(CommandEmpty, { children: "No results found." }),
172
+ /* @__PURE__ */ jsx(CommandGroup, {
173
+ className: "max-h-75 overflow-x-hidden overflow-y-auto",
174
+ children: options.map((option) => {
175
+ const isSelected = selectedValues.has(option.value);
176
+ return /* @__PURE__ */ jsxs(CommandItem, {
177
+ onSelect: () => onItemSelect(option, isSelected),
178
+ children: [
179
+ /* @__PURE__ */ jsx("div", {
180
+ className: cn("mr-2 flex size-4 items-center justify-center rounded-sm border border-primary", isSelected ? "bg-primary text-primary-foreground" : "opacity-50 [&_svg]:invisible"),
181
+ children: /* @__PURE__ */ jsx(Check, { className: "size-4" })
182
+ }),
183
+ option.icon && /* @__PURE__ */ jsx(option.icon, { className: "mr-2 size-4" }),
184
+ /* @__PURE__ */ jsx("span", {
185
+ className: "truncate",
186
+ children: option.label
187
+ }),
188
+ option.count !== void 0 && /* @__PURE__ */ jsx("span", {
189
+ className: "ml-auto font-mono text-xs",
190
+ children: option.count
191
+ })
192
+ ]
193
+ }, option.value);
194
+ })
195
+ }),
196
+ selectedValues.size > 0 && /* @__PURE__ */ jsxs(Fragment$1, { children: [/* @__PURE__ */ jsx(CommandSeparator, {}), /* @__PURE__ */ jsx(CommandGroup, { children: /* @__PURE__ */ jsx(CommandItem, {
197
+ onSelect: () => onReset(),
198
+ className: "justify-center text-center",
199
+ children: "Clear filters"
200
+ }) })] })
201
+ ]
202
+ })] });
203
+ }
204
+ /**
205
+ * @required displayName is required for auto feature detection
206
+ * @see "feature-detection.ts"
207
+ */
208
+ TableFacetedFilter.displayName = "TableFacetedFilter";
209
+ //#endregion
210
+ export { TableFacetedFilter, TableFacetedFilterContent, useTableFacetedFilter };
@@ -0,0 +1,29 @@
1
+ import { DataTableColumn } from "../types/index.js";
2
+ import { RowData } from "@tanstack/react-table";
3
+ //#region src/components/registry/niko-table/hooks/use-derived-column-title.d.ts
4
+ /**
5
+ * A hook that derives the title for a column filter component.
6
+ * It follows this priority order:
7
+ * 1. Provided title prop
8
+ * 2. Column metadata label (column.columnDef.meta?.label)
9
+ * 3. Formatted accessor key
10
+ *
11
+ * @param column - The table column
12
+ * @param accessorKey - The accessor key of the column
13
+ * @param title - Optional title override
14
+ * @returns The derived title string
15
+ *
16
+ * @example
17
+ * const derivedTitle = useDerivedColumnTitle(column, "firstName", "First Name")
18
+ * Returns "First Name"
19
+ *
20
+ * @example - With column.meta.label = "First Name"
21
+ * const derivedTitle = useDerivedColumnTitle(column, "firstName")
22
+ * Returns "First Name" from metadata
23
+ *
24
+ * @example - Without title or metadata
25
+ * const derivedTitle = useDerivedColumnTitle(column, "first_name")
26
+ * Returns "First Name" (formatted from accessorKey)
27
+ */
28
+ export declare function useDerivedColumnTitle<TData extends RowData, TValue = unknown>(column: DataTableColumn<TData, TValue> | undefined, accessorKey: string, title?: string): string;
29
+ //#endregion
@@ -0,0 +1,40 @@
1
+ import { formatLabel } from "../lib/format.js";
2
+ import * as React$1 from "react";
3
+ //#region src/components/registry/niko-table/hooks/use-derived-column-title.ts
4
+ /**
5
+ * A hook that derives the title for a column filter component.
6
+ * It follows this priority order:
7
+ * 1. Provided title prop
8
+ * 2. Column metadata label (column.columnDef.meta?.label)
9
+ * 3. Formatted accessor key
10
+ *
11
+ * @param column - The table column
12
+ * @param accessorKey - The accessor key of the column
13
+ * @param title - Optional title override
14
+ * @returns The derived title string
15
+ *
16
+ * @example
17
+ * const derivedTitle = useDerivedColumnTitle(column, "firstName", "First Name")
18
+ * Returns "First Name"
19
+ *
20
+ * @example - With column.meta.label = "First Name"
21
+ * const derivedTitle = useDerivedColumnTitle(column, "firstName")
22
+ * Returns "First Name" from metadata
23
+ *
24
+ * @example - Without title or metadata
25
+ * const derivedTitle = useDerivedColumnTitle(column, "first_name")
26
+ * Returns "First Name" (formatted from accessorKey)
27
+ */
28
+ function useDerivedColumnTitle(column, accessorKey, title) {
29
+ return React$1.useMemo(() => {
30
+ if (title) return title;
31
+ if (!column) return formatLabel(accessorKey);
32
+ return column.columnDef.meta?.label ?? formatLabel(accessorKey);
33
+ }, [
34
+ title,
35
+ column,
36
+ accessorKey
37
+ ]);
38
+ }
39
+ //#endregion
40
+ export { useDerivedColumnTitle };
@@ -0,0 +1,49 @@
1
+ import { DataTableInstance, Option } from "../types/index.js";
2
+ import { RowData } from "@tanstack/react-table";
3
+ //#region src/components/registry/niko-table/hooks/use-generated-options.d.ts
4
+ export interface GenerateOptionsConfig {
5
+ /**
6
+ * Whether to include counts for each option label
7
+ * @default true
8
+ */
9
+ showCounts?: boolean;
10
+ /**
11
+ * If true, recompute counts based on the filtered rows; otherwise use all core rows
12
+ * @default true
13
+ */
14
+ dynamicCounts?: boolean;
15
+ /**
16
+ * If true, only generate options from filtered rows. If false, generate from all rows.
17
+ * This controls which rows are used to generate the option list itself.
18
+ * Note: This is separate from dynamicCounts which controls count calculation.
19
+ * @default true
20
+ */
21
+ limitToFilteredRows?: boolean;
22
+ /**
23
+ * Only generate options for these column ids (if provided)
24
+ */
25
+ includeColumns?: string[];
26
+ /**
27
+ * Exclude these column ids from option generation
28
+ */
29
+ excludeColumns?: string[];
30
+ /**
31
+ * Optional cap on number of options per column (after sorting)
32
+ */
33
+ limitPerColumn?: number;
34
+ /**
35
+ * Default merge strategy when a column does not provide meta.mergeStrategy.
36
+ * Column-level mergeStrategy still takes precedence.
37
+ */
38
+ mergeStrategy?: "preserve" | "augment" | "replace";
39
+ }
40
+ /**
41
+ * Generate a map of options for select/multiSelect columns based on table data.
42
+ * Uses either filtered rows (dynamicCounts) or all core rows.
43
+ */
44
+ export declare function useGeneratedOptions<TData extends RowData>(table: DataTableInstance<TData>, config?: GenerateOptionsConfig): Record<string, Option[]>;
45
+ /**
46
+ * Convenience: generate options only for a specific column id
47
+ */
48
+ export declare function useGeneratedOptionsForColumn<TData extends RowData>(table: DataTableInstance<TData>, columnId: string, config?: GenerateOptionsConfig): Option[];
49
+ //#endregion