@ayasofyazilim/ui 0.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (236) hide show
  1. package/__mocks__/canvas.ts +8 -0
  2. package/components.json +21 -0
  3. package/eslint.config.js +4 -0
  4. package/jest-environment.js +37 -0
  5. package/jest.config.ts +47 -0
  6. package/jest.setup.ts +69 -0
  7. package/package.json +124 -0
  8. package/postcss.config.mjs +6 -0
  9. package/src/aria/index.tsx +1 -0
  10. package/src/aria/number-field.tsx +41 -0
  11. package/src/components/.gitkeep +0 -0
  12. package/src/components/accordion.tsx +66 -0
  13. package/src/components/alert-dialog.tsx +157 -0
  14. package/src/components/alert.tsx +70 -0
  15. package/src/components/aspect-ratio.tsx +11 -0
  16. package/src/components/avatar.tsx +53 -0
  17. package/src/components/badge.tsx +67 -0
  18. package/src/components/breadcrumb.tsx +109 -0
  19. package/src/components/button-group.tsx +83 -0
  20. package/src/components/button.tsx +68 -0
  21. package/src/components/calendar.tsx +219 -0
  22. package/src/components/card.tsx +92 -0
  23. package/src/components/carousel.tsx +241 -0
  24. package/src/components/chart.tsx +363 -0
  25. package/src/components/checkbox.tsx +32 -0
  26. package/src/components/collapsible.tsx +33 -0
  27. package/src/components/command.tsx +184 -0
  28. package/src/components/context-menu.tsx +252 -0
  29. package/src/components/dialog.tsx +144 -0
  30. package/src/components/drawer.tsx +135 -0
  31. package/src/components/dropdown-menu.tsx +258 -0
  32. package/src/components/empty.tsx +100 -0
  33. package/src/components/field.tsx +248 -0
  34. package/src/components/form.tsx +169 -0
  35. package/src/components/hover-card.tsx +44 -0
  36. package/src/components/input-group.tsx +170 -0
  37. package/src/components/input-otp.tsx +77 -0
  38. package/src/components/input.tsx +21 -0
  39. package/src/components/item.tsx +193 -0
  40. package/src/components/kbd.tsx +28 -0
  41. package/src/components/label.tsx +24 -0
  42. package/src/components/menubar.tsx +276 -0
  43. package/src/components/navigation-menu.tsx +168 -0
  44. package/src/components/pagination.tsx +130 -0
  45. package/src/components/popover.tsx +88 -0
  46. package/src/components/progress.tsx +31 -0
  47. package/src/components/radio-group.tsx +45 -0
  48. package/src/components/resizable.tsx +56 -0
  49. package/src/components/scroll-area.tsx +58 -0
  50. package/src/components/select.tsx +189 -0
  51. package/src/components/separator.tsx +28 -0
  52. package/src/components/sheet.tsx +140 -0
  53. package/src/components/sidebar.tsx +862 -0
  54. package/src/components/skeleton.tsx +13 -0
  55. package/src/components/slider.tsx +63 -0
  56. package/src/components/sonner.tsx +40 -0
  57. package/src/components/spinner.tsx +16 -0
  58. package/src/components/stepper.tsx +291 -0
  59. package/src/components/switch.tsx +31 -0
  60. package/src/components/table.tsx +133 -0
  61. package/src/components/tabs.tsx +66 -0
  62. package/src/components/textarea.tsx +18 -0
  63. package/src/components/toggle-group.tsx +83 -0
  64. package/src/components/toggle.tsx +47 -0
  65. package/src/components/tooltip.tsx +66 -0
  66. package/src/custom/action-button.tsx +48 -0
  67. package/src/custom/async-select.tsx +287 -0
  68. package/src/custom/awesome-not-found.tsx +116 -0
  69. package/src/custom/charts/area-chart.tsx +147 -0
  70. package/src/custom/charts/bar-chart.tsx +233 -0
  71. package/src/custom/charts/chart-card.tsx +103 -0
  72. package/src/custom/charts/index.tsx +16 -0
  73. package/src/custom/charts/pie-chart.tsx +168 -0
  74. package/src/custom/charts/radar-chart.tsx +126 -0
  75. package/src/custom/checkbox-tree.tsx +100 -0
  76. package/src/custom/combobox.tsx +296 -0
  77. package/src/custom/confirm-dialog.tsx +102 -0
  78. package/src/custom/country-selector.tsx +204 -0
  79. package/src/custom/date-picker/calendar-rac.tsx +109 -0
  80. package/src/custom/date-picker/datefield-rac.tsx +84 -0
  81. package/src/custom/date-picker/index.tsx +273 -0
  82. package/src/custom/date-picker/types/index.ts +4 -0
  83. package/src/custom/date-picker/utils/index.ts +42 -0
  84. package/src/custom/date-picker-old.tsx +50 -0
  85. package/src/custom/date-tooltip.tsx +98 -0
  86. package/src/custom/document-scanner/consts.ts +5 -0
  87. package/src/custom/document-scanner/corner-adjustment/action-buttons.tsx +33 -0
  88. package/src/custom/document-scanner/corner-adjustment/corner-handle.tsx +43 -0
  89. package/src/custom/document-scanner/corner-adjustment/hooks/use-corner-drag.ts +85 -0
  90. package/src/custom/document-scanner/corner-adjustment/index.tsx +125 -0
  91. package/src/custom/document-scanner/corner-adjustment/types.ts +53 -0
  92. package/src/custom/document-scanner/corner-adjustment/utils/clip-path.ts +22 -0
  93. package/src/custom/document-scanner/corner-adjustment/zoom-magnifier.tsx +115 -0
  94. package/src/custom/document-scanner/hooks/use-document-capture.ts +81 -0
  95. package/src/custom/document-scanner/hooks/use-document-scanner.ts +80 -0
  96. package/src/custom/document-scanner/hooks/use-perspective-crop.ts +38 -0
  97. package/src/custom/document-scanner/index.tsx +255 -0
  98. package/src/custom/document-scanner/lib.ts +407 -0
  99. package/src/custom/document-scanner/types.ts +205 -0
  100. package/src/custom/document-scanner/utils/perspective-correction.ts +139 -0
  101. package/src/custom/document-viewer/controllers.tsx +98 -0
  102. package/src/custom/document-viewer/index.tsx +43 -0
  103. package/src/custom/document-viewer/renderers/image.tsx +37 -0
  104. package/src/custom/document-viewer/renderers/index.tsx +2 -0
  105. package/src/custom/document-viewer/renderers/pdf.tsx +105 -0
  106. package/src/custom/email-input/domains.json +159 -0
  107. package/src/custom/email-input/email.tsx +229 -0
  108. package/src/custom/email-input/index.tsx +4 -0
  109. package/src/custom/email-input/types.ts +104 -0
  110. package/src/custom/file-uploader.tsx +541 -0
  111. package/src/custom/filter-component/fields/async-select.tsx +33 -0
  112. package/src/custom/filter-component/fields/date.tsx +60 -0
  113. package/src/custom/filter-component/fields/multi-select.tsx +30 -0
  114. package/src/custom/filter-component/index.tsx +217 -0
  115. package/src/custom/image-canvas.tsx +260 -0
  116. package/src/custom/json-editor.tsx +22 -0
  117. package/src/custom/master-data-grid/components/dialogs/column-settings-dialog.tsx +100 -0
  118. package/src/custom/master-data-grid/components/dialogs/index.ts +1 -0
  119. package/src/custom/master-data-grid/components/filters/client-filter.tsx +368 -0
  120. package/src/custom/master-data-grid/components/filters/filter-input.tsx +256 -0
  121. package/src/custom/master-data-grid/components/filters/index.ts +3 -0
  122. package/src/custom/master-data-grid/components/filters/inline-column-filter.tsx +233 -0
  123. package/src/custom/master-data-grid/components/filters/multi-filter-dialog.tsx +90 -0
  124. package/src/custom/master-data-grid/components/filters/server-filter.tsx +255 -0
  125. package/src/custom/master-data-grid/components/master-data-grid.tsx +472 -0
  126. package/src/custom/master-data-grid/components/pagination/index.ts +1 -0
  127. package/src/custom/master-data-grid/components/pagination/pagination.tsx +178 -0
  128. package/src/custom/master-data-grid/components/table/cell-renderer.tsx +634 -0
  129. package/src/custom/master-data-grid/components/table/header-cell.tsx +162 -0
  130. package/src/custom/master-data-grid/components/table/index.ts +4 -0
  131. package/src/custom/master-data-grid/components/table/table-body-renderer.tsx +113 -0
  132. package/src/custom/master-data-grid/components/table/virtual-body.tsx +138 -0
  133. package/src/custom/master-data-grid/components/toolbar/index.ts +1 -0
  134. package/src/custom/master-data-grid/components/toolbar/toolbar.tsx +314 -0
  135. package/src/custom/master-data-grid/hooks/index.ts +3 -0
  136. package/src/custom/master-data-grid/hooks/use-columns.tsx +332 -0
  137. package/src/custom/master-data-grid/hooks/use-editing.ts +106 -0
  138. package/src/custom/master-data-grid/hooks/use-table-state-reducer.ts +157 -0
  139. package/src/custom/master-data-grid/hooks/use-table-state.ts +31 -0
  140. package/src/custom/master-data-grid/index.ts +16 -0
  141. package/src/custom/master-data-grid/types.ts +466 -0
  142. package/src/custom/master-data-grid/utils/column-generator.tsx +306 -0
  143. package/src/custom/master-data-grid/utils/export-utils.ts +67 -0
  144. package/src/custom/master-data-grid/utils/filter-fns.ts +290 -0
  145. package/src/custom/master-data-grid/utils/index.ts +8 -0
  146. package/src/custom/master-data-grid/utils/pinning-utils.ts +88 -0
  147. package/src/custom/master-data-grid/utils/translation-utils.ts +42 -0
  148. package/src/custom/multi-select.tsx +432 -0
  149. package/src/custom/password-input.tsx +194 -0
  150. package/src/custom/phone-input.tsx +172 -0
  151. package/src/custom/schema-form/custom/index.tsx +1 -0
  152. package/src/custom/schema-form/custom/label.tsx +53 -0
  153. package/src/custom/schema-form/fields/base-input-field.tsx +82 -0
  154. package/src/custom/schema-form/fields/field.tsx +67 -0
  155. package/src/custom/schema-form/fields/index.tsx +5 -0
  156. package/src/custom/schema-form/fields/object.tsx +12 -0
  157. package/src/custom/schema-form/fields/table-array/array-field-item.tsx +90 -0
  158. package/src/custom/schema-form/fields/table-array/array-field-template.tsx +115 -0
  159. package/src/custom/schema-form/index.tsx +259 -0
  160. package/src/custom/schema-form/templates/description.tsx +20 -0
  161. package/src/custom/schema-form/templates/index.tsx +2 -0
  162. package/src/custom/schema-form/templates/submit.tsx +32 -0
  163. package/src/custom/schema-form/types.ts +64 -0
  164. package/src/custom/schema-form/utils/index.ts +4 -0
  165. package/src/custom/schema-form/utils/schema-dependency.ts +655 -0
  166. package/src/custom/schema-form/utils/schemas.ts +289 -0
  167. package/src/custom/schema-form/utils/validation.ts +23 -0
  168. package/src/custom/schema-form/widgets/boolean.tsx +77 -0
  169. package/src/custom/schema-form/widgets/combobox.tsx +274 -0
  170. package/src/custom/schema-form/widgets/date.tsx +59 -0
  171. package/src/custom/schema-form/widgets/email.tsx +34 -0
  172. package/src/custom/schema-form/widgets/index.tsx +10 -0
  173. package/src/custom/schema-form/widgets/password.tsx +40 -0
  174. package/src/custom/schema-form/widgets/phone.tsx +40 -0
  175. package/src/custom/schema-form/widgets/select.tsx +105 -0
  176. package/src/custom/schema-form/widgets/selectable.tsx +25 -0
  177. package/src/custom/schema-form/widgets/string-array.tsx +296 -0
  178. package/src/custom/schema-form/widgets/url.tsx +56 -0
  179. package/src/custom/section-layout-v2.tsx +212 -0
  180. package/src/custom/select-tabs.tsx +109 -0
  181. package/src/custom/selectable.tsx +316 -0
  182. package/src/custom/stepper.tsx +236 -0
  183. package/src/custom/tab-layout.tsx +213 -0
  184. package/src/custom/tanstack-table/fields/index.tsx +12 -0
  185. package/src/custom/tanstack-table/fields/tanstack-table-action-dialogs.tsx +89 -0
  186. package/src/custom/tanstack-table/fields/tanstack-table-column-header.tsx +66 -0
  187. package/src/custom/tanstack-table/fields/tanstack-table-filter-date.tsx +180 -0
  188. package/src/custom/tanstack-table/fields/tanstack-table-filter-faceted.tsx +158 -0
  189. package/src/custom/tanstack-table/fields/tanstack-table-filter-text.tsx +76 -0
  190. package/src/custom/tanstack-table/fields/tanstack-table-pagination.tsx +136 -0
  191. package/src/custom/tanstack-table/fields/tanstack-table-plain-table.tsx +142 -0
  192. package/src/custom/tanstack-table/fields/tanstack-table-row-actions-confirmation.tsx +77 -0
  193. package/src/custom/tanstack-table/fields/tanstack-table-row-actions-custom-dialog.tsx +87 -0
  194. package/src/custom/tanstack-table/fields/tanstack-table-row-actions.tsx +151 -0
  195. package/src/custom/tanstack-table/fields/tanstack-table-table-actions-custom-dialog.tsx +88 -0
  196. package/src/custom/tanstack-table/fields/tanstack-table-table-actions-schemaform-dialog.tsx +47 -0
  197. package/src/custom/tanstack-table/fields/tanstack-table-toolbar.tsx +143 -0
  198. package/src/custom/tanstack-table/fields/tanstack-table-view-options.tsx +171 -0
  199. package/src/custom/tanstack-table/index.tsx +244 -0
  200. package/src/custom/tanstack-table/types/index.ts +328 -0
  201. package/src/custom/tanstack-table/utils/cell-with-actions.tsx +21 -0
  202. package/src/custom/tanstack-table/utils/column-names.ts +26 -0
  203. package/src/custom/tanstack-table/utils/columns-by-row-data.tsx +312 -0
  204. package/src/custom/tanstack-table/utils/editable-columns-by-row-data.tsx +219 -0
  205. package/src/custom/tanstack-table/utils/faceted-boolean-options.tsx +22 -0
  206. package/src/custom/tanstack-table/utils/index.tsx +10 -0
  207. package/src/custom/tanstack-table/utils/pinning-styles.ts +57 -0
  208. package/src/custom/tanstack-table/utils/table.tsx +83 -0
  209. package/src/custom/tanstack-table/utils/test-conditions.ts +17 -0
  210. package/src/custom/timeline.tsx +208 -0
  211. package/src/custom/tree.tsx +200 -0
  212. package/src/custom/tscanify/browser.ts +66 -0
  213. package/src/custom/tscanify/index.ts +51 -0
  214. package/src/custom/tscanify/tscanify-browser.ts +522 -0
  215. package/src/custom/tscanify/tscanify.ts +262 -0
  216. package/src/custom/tscanify/types.ts +22 -0
  217. package/src/custom/webcam.tsx +737 -0
  218. package/src/hooks/.gitkeep +0 -0
  219. package/src/hooks/use-callback-ref.ts +27 -0
  220. package/src/hooks/use-controllable-state.ts +67 -0
  221. package/src/hooks/use-debounce.ts +19 -0
  222. package/src/hooks/use-is-visible.ts +23 -0
  223. package/src/hooks/use-media-query.ts +21 -0
  224. package/src/hooks/use-mobile.ts +21 -0
  225. package/src/hooks/use-on-window-resize.ts +15 -0
  226. package/src/hooks/use-scroll.tsx +22 -0
  227. package/src/lib/utils.ts +61 -0
  228. package/src/lib/zod.ts +2 -0
  229. package/src/styles/core.css +57 -0
  230. package/src/styles/globals.css +130 -0
  231. package/src/test/email-input.test.tsx +217 -0
  232. package/src/test/password-input.test.tsx +92 -0
  233. package/src/test/select-tabs.test.tsx +302 -0
  234. package/src/test/selectable.test.tsx +1093 -0
  235. package/tsconfig.json +13 -0
  236. package/tsconfig.lint.json +8 -0
@@ -0,0 +1,472 @@
1
+ "use client";
2
+
3
+ import type { FilterFn } from "@tanstack/react-table";
4
+ import {
5
+ flexRender,
6
+ getCoreRowModel,
7
+ getExpandedRowModel,
8
+ getFilteredRowModel,
9
+ getGroupedRowModel,
10
+ getPaginationRowModel,
11
+ getSortedRowModel,
12
+ useReactTable,
13
+ } from "@tanstack/react-table";
14
+ import { useCallback, useMemo, useRef, useState } from "react";
15
+
16
+ import { Skeleton } from "../../../components/skeleton";
17
+ import {
18
+ Table,
19
+ TableHead,
20
+ TableHeader,
21
+ TableRow,
22
+ } from "../../../components/table";
23
+ import { cn } from "../../../lib/utils";
24
+ import { useColumns } from "../hooks/use-columns";
25
+ import { useEditing } from "../hooks/use-editing";
26
+ import { useTableStateReducer } from "../hooks/use-table-state-reducer";
27
+ import type { MasterDataGridConfig, MasterDataGridProps } from "../types";
28
+ import { exportToCSV } from "../utils/export-utils";
29
+ import {
30
+ getPinningHeaderClassNames,
31
+ getPinningHeaderStyles,
32
+ } from "../utils/pinning-utils";
33
+ import { getTranslations } from "../utils/translation-utils";
34
+ import { ColumnSettingsDialog } from "./dialogs/column-settings-dialog";
35
+ import { ServerFilterContent } from "./filters/server-filter";
36
+ import { Pagination } from "./pagination";
37
+ import { TableBodyRenderer, VirtualBody } from "./table";
38
+ import { Toolbar } from "./toolbar";
39
+
40
+ const COMPONENT_NAME = "MasterDataGrid";
41
+
42
+ const logError = (message: string, data?: unknown) => {
43
+ console.error(`[${COMPONENT_NAME}] ${message}`, data || "");
44
+ };
45
+
46
+ const logWarning = (message: string, data?: unknown) => {
47
+ console.warn(`[${COMPONENT_NAME}] ${message}`, data || "");
48
+ };
49
+
50
+ export function MasterDataGrid<TData = Record<string, unknown>>({
51
+ data,
52
+ config,
53
+ onDataChange,
54
+ }: MasterDataGridProps<TData>) {
55
+ const {
56
+ schema,
57
+ columns: customColumns,
58
+ t,
59
+ enableSearch = true,
60
+ enableSorting = true,
61
+ enableFiltering = true,
62
+ enableGrouping = true,
63
+ enablePinning = true,
64
+ enableResizing = true,
65
+ enableColumnVisibility = true,
66
+ enableRowSelection = false,
67
+ enableVirtualization = false,
68
+ enableExport = true,
69
+ enablePagination = true,
70
+ pageSize = 10,
71
+ pageSizeOptions = [10, 20, 30, 40, 50],
72
+ getRowId,
73
+ serverFilters,
74
+ serverFilterLocation = "toolbar",
75
+ pinning = {
76
+ right: ["actions"],
77
+ },
78
+ } = config;
79
+
80
+ const configWithDefaults: MasterDataGridConfig<TData> = {
81
+ ...config,
82
+ enableSearch,
83
+ enableSorting,
84
+ enableFiltering,
85
+ enableGrouping,
86
+ enablePinning,
87
+ enableResizing,
88
+ enableColumnVisibility,
89
+ enableRowSelection,
90
+ enableVirtualization,
91
+ enableExport,
92
+ enablePagination,
93
+ serverFilters,
94
+ serverFilterLocation,
95
+ pinning,
96
+ };
97
+
98
+ const {
99
+ tableState,
100
+ setSorting,
101
+ setColumnFilters,
102
+ setColumnVisibility,
103
+ setRowSelection,
104
+ setColumnPinning,
105
+ setGrouping,
106
+ setExpanded,
107
+ setPagination,
108
+ updateEditingRows,
109
+ resetToDefaults,
110
+ } = useTableStateReducer(configWithDefaults, pageSize);
111
+
112
+ const [globalFilter, setGlobalFilter] = useState("");
113
+
114
+ const [columnSettingsOpen, setColumnSettingsOpen] = useState(false);
115
+
116
+ const configRef = useRef(configWithDefaults);
117
+ configRef.current = configWithDefaults;
118
+
119
+ const {
120
+ editingRowsRef,
121
+ startEditingRow,
122
+ cancelEditingRow,
123
+ saveEditingRow,
124
+ updateCellValue,
125
+ } = useEditing({
126
+ data,
127
+ getRowId,
128
+ onDataChange,
129
+ editingRows: tableState.editingRows || {},
130
+ setEditingRows: updateEditingRows,
131
+ editing: configWithDefaults.editing,
132
+ });
133
+
134
+ const globalFilterFn = useMemo<FilterFn<TData>>(
135
+ () => (row, _, filterValue) => {
136
+ const search = String(filterValue).toLowerCase();
137
+ return Object.values(row.original as Record<string, unknown>).some(
138
+ (value) => {
139
+ if (value == null) return false;
140
+ return String(value).toLowerCase().includes(search);
141
+ }
142
+ );
143
+ },
144
+ []
145
+ );
146
+
147
+ const columns = useColumns({
148
+ config: configWithDefaults,
149
+ configRef,
150
+ schema,
151
+ customColumns,
152
+ enableRowSelection,
153
+ enableColumnVisibility,
154
+ editingRowsRef,
155
+ updateCellValue,
156
+ getRowId,
157
+ t,
158
+ startEditingRow,
159
+ cancelEditingRow,
160
+ saveEditingRow,
161
+ });
162
+
163
+ const table = useReactTable({
164
+ data,
165
+ columns,
166
+ state: {
167
+ sorting: tableState.sorting,
168
+ columnFilters: tableState.columnFilters,
169
+ columnVisibility: tableState.columnVisibility,
170
+ rowSelection: tableState.rowSelection,
171
+ columnPinning: tableState.columnPinning,
172
+ grouping: tableState.grouping,
173
+ expanded: tableState.expanded,
174
+ pagination: tableState.pagination,
175
+ globalFilter,
176
+ },
177
+ onSortingChange: (updater) => {
178
+ const newSorting =
179
+ typeof updater === "function" ? updater(tableState.sorting) : updater;
180
+ setSorting(newSorting);
181
+ configWithDefaults.onSortingChange?.(newSorting);
182
+ },
183
+ onColumnFiltersChange: (updater) => {
184
+ const newFilters =
185
+ typeof updater === "function"
186
+ ? updater(tableState.columnFilters)
187
+ : updater;
188
+ setColumnFilters(newFilters);
189
+ configWithDefaults.onFilteringChange?.(newFilters);
190
+ },
191
+ onColumnVisibilityChange: (updater) => {
192
+ const newVisibility =
193
+ typeof updater === "function"
194
+ ? updater(tableState.columnVisibility)
195
+ : updater;
196
+ setColumnVisibility(newVisibility);
197
+ },
198
+ onRowSelectionChange: (updater) => {
199
+ const newSelection =
200
+ typeof updater === "function"
201
+ ? updater(tableState.rowSelection)
202
+ : updater;
203
+ setRowSelection(newSelection);
204
+
205
+ if (configWithDefaults.selection?.onSelectionChange) {
206
+ const selectedRows = table
207
+ .getSelectedRowModel()
208
+ .rows.map((row) => row.original);
209
+ configWithDefaults.selection.onSelectionChange(selectedRows);
210
+ }
211
+ },
212
+ onColumnPinningChange: (updater) => {
213
+ const newPinning =
214
+ typeof updater === "function"
215
+ ? updater(tableState.columnPinning)
216
+ : updater;
217
+ setColumnPinning(newPinning);
218
+ },
219
+ onGroupingChange: (updater) => {
220
+ const newGrouping =
221
+ typeof updater === "function" ? updater(tableState.grouping) : updater;
222
+ setGrouping(newGrouping);
223
+ },
224
+ onExpandedChange: (updater) => {
225
+ const newExpanded =
226
+ typeof updater === "function" ? updater(tableState.expanded) : updater;
227
+ setExpanded(newExpanded);
228
+ },
229
+ onPaginationChange: (updater) => {
230
+ const newPagination =
231
+ typeof updater === "function"
232
+ ? updater(tableState.pagination)
233
+ : updater;
234
+ setPagination(newPagination);
235
+ configWithDefaults.onPaginationChange?.(newPagination);
236
+ },
237
+ onGlobalFilterChange: setGlobalFilter,
238
+ globalFilterFn: globalFilterFn,
239
+ getCoreRowModel: getCoreRowModel(),
240
+ getSortedRowModel: enableSorting ? getSortedRowModel() : undefined,
241
+ getFilteredRowModel: enableFiltering ? getFilteredRowModel() : undefined,
242
+ getPaginationRowModel: enablePagination
243
+ ? getPaginationRowModel()
244
+ : undefined,
245
+ getGroupedRowModel: enableGrouping ? getGroupedRowModel() : undefined,
246
+ getExpandedRowModel: getExpandedRowModel(),
247
+ manualSorting: configWithDefaults.manualSorting,
248
+ manualFiltering: configWithDefaults.manualFiltering,
249
+ manualPagination:
250
+ configWithDefaults.manualPagination ??
251
+ configWithDefaults.rowCount != null,
252
+ rowCount: configWithDefaults.rowCount,
253
+ enableSorting,
254
+ enableFilters: enableFiltering,
255
+ enableGrouping,
256
+ enableColumnPinning: enablePinning,
257
+ enableColumnResizing: enableResizing,
258
+ columnResizeMode: "onChange",
259
+ enableHiding: enableColumnVisibility,
260
+ enableRowSelection: Boolean(enableRowSelection),
261
+ getRowId: getRowId ?? ((_, index) => String(index)),
262
+ getRowCanExpand: configWithDefaults.expansion?.enabled
263
+ ? () => true
264
+ : undefined,
265
+ });
266
+
267
+ const selectedRows = useMemo(
268
+ () => table.getSelectedRowModel().rows.map((row) => row.original),
269
+ [table]
270
+ );
271
+
272
+ const handleExport = useCallback(
273
+ (format: string) => {
274
+ try {
275
+ if (configWithDefaults.export?.customExport) {
276
+ configWithDefaults.export.customExport(data, format);
277
+ } else if (format === "csv") {
278
+ exportToCSV(table, { filename: configWithDefaults.export?.filename });
279
+ } else {
280
+ logWarning(`Unsupported export format: ${format}`);
281
+ }
282
+ } catch (error) {
283
+ logError("Export failed:", error);
284
+ }
285
+ },
286
+ [table, data, configWithDefaults.export]
287
+ );
288
+
289
+ const handleReset = useCallback(() => {
290
+ resetToDefaults();
291
+ table.resetColumnOrder();
292
+ table.resetColumnSizing();
293
+ table.resetGlobalFilter();
294
+ setGlobalFilter("");
295
+ }, [table, resetToDefaults]);
296
+
297
+ if (configWithDefaults.loading) {
298
+ return (
299
+ <div className={cn("space-y-4", configWithDefaults.containerClassName)}>
300
+ <Skeleton className="h-10 w-full" />
301
+ <Skeleton className="h-96 w-full" />
302
+ </div>
303
+ );
304
+ }
305
+
306
+ if (
307
+ data.length === 0 &&
308
+ !configWithDefaults.loading &&
309
+ configWithDefaults.emptyComponent
310
+ ) {
311
+ return (
312
+ <div
313
+ className={cn(
314
+ "flex items-center justify-center h-96",
315
+ configWithDefaults.containerClassName
316
+ )}
317
+ >
318
+ {configWithDefaults.emptyComponent}
319
+ </div>
320
+ );
321
+ }
322
+
323
+ const rows = table.getRowModel().rows;
324
+
325
+ return (
326
+ <div
327
+ className={cn(
328
+ "flex flex-col gap-2 h-full",
329
+ configWithDefaults.containerClassName
330
+ )}
331
+ >
332
+ <Toolbar
333
+ table={table}
334
+ serverFilters={
335
+ serverFilterLocation === "toolbar" ? serverFilters : undefined
336
+ }
337
+ config={configWithDefaults}
338
+ selectedRows={selectedRows}
339
+ onExport={enableExport ? handleExport : undefined}
340
+ onRefresh={configWithDefaults.onRefresh}
341
+ onReset={handleReset}
342
+ onOpenColumnSettings={() => setColumnSettingsOpen(true)}
343
+ />
344
+
345
+ <div
346
+ className={cn(
347
+ "relative w-full border rounded-md overflow-hidden flex",
348
+ configWithDefaults.className
349
+ )}
350
+ style={{ height: enableVirtualization ? "600px" : "auto" }}
351
+ >
352
+ {enableVirtualization ? (
353
+ <Table
354
+ className={cn(
355
+ "border-separate border-spacing-0",
356
+ configWithDefaults.tableClassName
357
+ )}
358
+ >
359
+ <TableHeader
360
+ className={cn(
361
+ "sticky top-0 z-10 bg-background",
362
+ configWithDefaults.headerClassName
363
+ )}
364
+ >
365
+ {table.getHeaderGroups().map((headerGroup) => (
366
+ <TableRow key={headerGroup.id}>
367
+ {headerGroup.headers.map((header) => (
368
+ <TableHead
369
+ key={header.id}
370
+ style={getPinningHeaderStyles(header)}
371
+ className={cn(
372
+ getPinningHeaderClassNames(header),
373
+ "border-b border-r"
374
+ )}
375
+ >
376
+ {header.isPlaceholder
377
+ ? null
378
+ : flexRender(
379
+ header.column.columnDef.header,
380
+ header.getContext()
381
+ )}
382
+ </TableHead>
383
+ ))}
384
+ </TableRow>
385
+ ))}
386
+ </TableHeader>
387
+ <VirtualBody
388
+ rows={rows}
389
+ estimateSize={configWithDefaults.virtualization?.estimateSize}
390
+ overscan={configWithDefaults.virtualization?.overscan}
391
+ rowClassName={configWithDefaults.rowClassName}
392
+ cellClassName={configWithDefaults.cellClassName}
393
+ editingRows={tableState.editingRows}
394
+ getRowId={getRowId}
395
+ editingEnabled={configWithDefaults.editing?.enabled}
396
+ expansionEnabled={configWithDefaults.expansion?.enabled}
397
+ expansionRenderContent={
398
+ configWithDefaults.expansion?.renderContent
399
+ }
400
+ />
401
+ </Table>
402
+ ) : (
403
+ <Table
404
+ className={cn(
405
+ "border-spacing-0",
406
+ configWithDefaults.tableClassName
407
+ )}
408
+ wrapperClassName="overflow-auto size-full"
409
+ >
410
+ <TableHeader
411
+ className={cn(
412
+ "sticky top-0 z-10 bg-background",
413
+ configWithDefaults.headerClassName
414
+ )}
415
+ >
416
+ {table.getHeaderGroups().map((headerGroup) => (
417
+ <TableRow key={headerGroup.id}>
418
+ {headerGroup.headers.map((header) => (
419
+ <TableHead
420
+ key={header.id}
421
+ style={getPinningHeaderStyles(header)}
422
+ className={cn(
423
+ getPinningHeaderClassNames(header),
424
+ "has-[button]:px-0 not-last:border-r"
425
+ )}
426
+ >
427
+ {header.isPlaceholder
428
+ ? null
429
+ : flexRender(
430
+ header.column.columnDef.header,
431
+ header.getContext()
432
+ )}
433
+ </TableHead>
434
+ ))}
435
+ </TableRow>
436
+ ))}
437
+ </TableHeader>
438
+ <TableBodyRenderer
439
+ table={table}
440
+ rows={rows}
441
+ rowClassName={configWithDefaults.rowClassName}
442
+ cellClassName={configWithDefaults.cellClassName}
443
+ editingRows={tableState.editingRows}
444
+ getRowId={getRowId}
445
+ editingEnabled={configWithDefaults.editing?.enabled}
446
+ emptyMessage={getTranslations("table.noResults", t)}
447
+ expansionEnabled={configWithDefaults.expansion?.enabled}
448
+ expansionRenderContent={
449
+ configWithDefaults.expansion?.renderContent
450
+ }
451
+ bodyClassName={configWithDefaults.bodyClassName}
452
+ />
453
+ </Table>
454
+ )}
455
+ {serverFilterLocation !== "toolbar" && (
456
+ <div className="border-l hidden lg:block">
457
+ <ServerFilterContent table={table} config={config} />
458
+ </div>
459
+ )}
460
+ </div>
461
+ {enablePagination && (
462
+ <Pagination table={table} pageSizeOptions={pageSizeOptions} t={t} />
463
+ )}
464
+ <ColumnSettingsDialog
465
+ open={columnSettingsOpen}
466
+ onOpenChange={setColumnSettingsOpen}
467
+ table={table}
468
+ t={t}
469
+ />
470
+ </div>
471
+ );
472
+ }
@@ -0,0 +1 @@
1
+ export { Pagination } from "./pagination";
@@ -0,0 +1,178 @@
1
+ "use client";
2
+ "use no memo";
3
+ import type { Table } from "@tanstack/react-table";
4
+ import {
5
+ ChevronLeftIcon,
6
+ ChevronRightIcon,
7
+ ChevronsLeftIcon,
8
+ ChevronsRightIcon,
9
+ } from "lucide-react";
10
+ import { usePathname, useRouter, useSearchParams } from "next/navigation";
11
+ import { useEffect } from "react";
12
+ import { Button } from "../../../../components/button";
13
+ import {
14
+ Select,
15
+ SelectContent,
16
+ SelectItem,
17
+ SelectTrigger,
18
+ SelectValue,
19
+ } from "../../../../components/select";
20
+ import { getTranslations } from "../../utils/translation-utils";
21
+ import { MasterDataGridResources } from "../../types";
22
+
23
+ interface PaginationProps<TData> {
24
+ table: Table<TData>;
25
+ pageSizeOptions?: number[];
26
+ t?: MasterDataGridResources;
27
+ }
28
+
29
+ export function Pagination<TData>({
30
+ table,
31
+ pageSizeOptions = [10, 20, 30, 40, 50],
32
+ t,
33
+ }: PaginationProps<TData>) {
34
+ const { replace } = useRouter();
35
+ const pathname = usePathname();
36
+ const searchParams = useSearchParams();
37
+ const pagination = table.getState().pagination;
38
+
39
+ const allPageSizeOptions = pageSizeOptions.includes(pagination.pageSize)
40
+ ? pageSizeOptions
41
+ : [...pageSizeOptions, pagination.pageSize].sort((a, b) => a - b);
42
+
43
+ useEffect(() => {
44
+ const params = new URLSearchParams(searchParams.toString());
45
+ const skipCount = pagination.pageIndex * pagination.pageSize;
46
+ const maxResultCount = pagination.pageSize;
47
+
48
+ const currentSkipCount = Number(searchParams?.get("skipCount")) || 0;
49
+ const currentMaxResultCount =
50
+ Number(searchParams?.get("maxResultCount")) || 10;
51
+
52
+ const needsUpdate =
53
+ currentSkipCount !== skipCount ||
54
+ currentMaxResultCount !== maxResultCount;
55
+
56
+ if (!needsUpdate) return;
57
+
58
+ if (currentMaxResultCount !== maxResultCount) {
59
+ params.set("maxResultCount", maxResultCount.toString());
60
+ }
61
+ if (currentSkipCount !== skipCount) {
62
+ params.set("skipCount", skipCount.toString());
63
+ }
64
+
65
+ if (maxResultCount === 10) {
66
+ params.delete("maxResultCount");
67
+ }
68
+ if (skipCount === 0) {
69
+ params.delete("skipCount");
70
+ }
71
+
72
+ replace(`${pathname}?${params.toString()}`);
73
+ }, [
74
+ pagination.pageIndex,
75
+ pagination.pageSize,
76
+ pathname,
77
+ searchParams,
78
+ replace,
79
+ ]);
80
+
81
+ return (
82
+ <div className="flex items-center flex-wrap gap-4">
83
+ {table.getIsSomeRowsSelected() && (
84
+ <div className="text-sm text-muted-foreground text-nowrap">
85
+ {table.getFilteredSelectedRowModel().rows.length}{" "}
86
+ {getTranslations("pagination.of", t)}{" "}
87
+ {table.getFilteredRowModel().rows.length}{" "}
88
+ {getTranslations("pagination.rowsSelected", t)}
89
+ </div>
90
+ )}
91
+
92
+ <div className="flex items-center flex-wrap gap-2 w-full sm:w-auto ml-auto">
93
+ <div className="flex items-center w-full justify-between gap-2 sm:w-auto md:mr-4">
94
+ <p className="text-sm font-medium">
95
+ {getTranslations("pagination.rowsPerPage", t)}
96
+ </p>
97
+ <Select
98
+ value={`${pagination.pageSize}`}
99
+ onValueChange={(value) => {
100
+ table.setPageSize(Number(value));
101
+ }}
102
+ >
103
+ <SelectTrigger className="h-8 w-[70px]">
104
+ <SelectValue placeholder={pagination.pageSize} />
105
+ </SelectTrigger>
106
+ <SelectContent side="top">
107
+ {allPageSizeOptions.map((pageSize) => (
108
+ <SelectItem key={pageSize} value={`${pageSize}`}>
109
+ {pageSize}
110
+ </SelectItem>
111
+ ))}
112
+ </SelectContent>
113
+ </Select>
114
+ </div>
115
+
116
+ <div className="flex items-center justify-center text-sm font-medium mr-auto">
117
+ {getTranslations("pagination.page", t)}{" "}
118
+ {table.getPageCount() === 0 ? 0 : pagination.pageIndex + 1}{" "}
119
+ {getTranslations("pagination.of", t)} {table.getPageCount()}
120
+ </div>
121
+
122
+ <div className="flex items-center gap-x-2">
123
+ <Button
124
+ variant="outline"
125
+ className="hidden h-8 w-8 p-0 lg:flex"
126
+ onClick={() => table.setPageIndex(0)}
127
+ disabled={!table.getCanPreviousPage()}
128
+ title={getTranslations("pagination.firstPage", t)}
129
+ >
130
+ <span className="sr-only">
131
+ {getTranslations("pagination.firstPage", t)}
132
+ </span>
133
+ <ChevronsLeftIcon className="h-4 w-4" />
134
+ </Button>
135
+
136
+ <Button
137
+ variant="outline"
138
+ className="h-8 w-8 p-0"
139
+ onClick={() => table.previousPage()}
140
+ disabled={!table.getCanPreviousPage()}
141
+ title={getTranslations("pagination.previousPage", t)}
142
+ >
143
+ <span className="sr-only">
144
+ {getTranslations("pagination.previousPage", t)}
145
+ </span>
146
+ <ChevronLeftIcon className="h-4 w-4" />
147
+ </Button>
148
+
149
+ <Button
150
+ variant="outline"
151
+ className="h-8 w-8 p-0"
152
+ onClick={() => table.nextPage()}
153
+ disabled={!table.getCanNextPage()}
154
+ title={getTranslations("pagination.nextPage", t)}
155
+ >
156
+ <span className="sr-only">
157
+ {getTranslations("pagination.nextPage", t)}
158
+ </span>
159
+ <ChevronRightIcon className="h-4 w-4" />
160
+ </Button>
161
+
162
+ <Button
163
+ variant="outline"
164
+ className="hidden h-8 w-8 p-0 lg:flex"
165
+ onClick={() => table.setPageIndex(table.getPageCount() - 1)}
166
+ disabled={!table.getCanNextPage()}
167
+ title={getTranslations("pagination.lastPage", t)}
168
+ >
169
+ <span className="sr-only">
170
+ {getTranslations("pagination.lastPage", t)}
171
+ </span>
172
+ <ChevronsRightIcon className="h-4 w-4" />
173
+ </Button>
174
+ </div>
175
+ </div>
176
+ </div>
177
+ );
178
+ }