@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,180 @@
1
+ import { CirclePlusIcon } from "lucide-react";
2
+ import { Column } from "@tanstack/react-table";
3
+
4
+ import { useEffect, useState } from "react";
5
+ import { Button } from "@repo/ayasofyazilim-ui/components/button";
6
+ import {
7
+ Popover,
8
+ PopoverContent,
9
+ PopoverTrigger,
10
+ } from "@repo/ayasofyazilim-ui/components/popover";
11
+ import { Separator } from "@repo/ayasofyazilim-ui/components/separator";
12
+ import { TanstackTableDateFilterType } from "../types";
13
+ import {
14
+ DatePicker,
15
+ DateRangePicker,
16
+ } from "@repo/ayasofyazilim-ui/custom/date-picker";
17
+ import { DateRange } from "@repo/ayasofyazilim-ui/custom/date-picker/types";
18
+
19
+ interface TanstackTableDateFilterProps<TData, TValue> {
20
+ accessorKey: string;
21
+ column?: Column<TData, TValue>;
22
+ dateItem: TanstackTableDateFilterType;
23
+ onFilter: (accessorKey: string, selectedValues: string) => void;
24
+ onFilterMultiple: (
25
+ filter: {
26
+ accessorKey: string;
27
+ selectedValues: string;
28
+ }[]
29
+ ) => void;
30
+ params: URLSearchParams;
31
+ }
32
+
33
+ export function TanstackTableDateFilter<TData, TValue>({
34
+ column,
35
+ accessorKey,
36
+ params,
37
+ onFilter,
38
+ onFilterMultiple,
39
+ dateItem,
40
+ }: TanstackTableDateFilterProps<TData, TValue>) {
41
+ const title = column?.columnDef?.meta?.toString() || accessorKey;
42
+
43
+ // If endAccessorKey is provided, then it is a date range. Otherwise it is a single date.
44
+ const [date, setDate] = useState<Date | DateRange | undefined>(
45
+ dateItem?.endAccessorKey
46
+ ? {
47
+ start: params?.get(dateItem.startAccessorKey)
48
+ ? new Date(params?.get(dateItem.startAccessorKey) as string)
49
+ : undefined,
50
+ end: params?.get(dateItem?.endAccessorKey)
51
+ ? new Date(params?.get(dateItem.endAccessorKey) as string)
52
+ : undefined,
53
+ }
54
+ : params?.get(dateItem.startAccessorKey)
55
+ ? new Date(params?.get(dateItem.startAccessorKey) as string)
56
+ : undefined
57
+ );
58
+
59
+ useEffect(() => {
60
+ function isFilterChanged(accessorKey: string, date: Date | undefined) {
61
+ return params.get(accessorKey) !== date?.toISOString();
62
+ }
63
+
64
+ function isFiltered(accessorKey: string) {
65
+ return !!params.get(accessorKey);
66
+ }
67
+
68
+ const filter: {
69
+ accessorKey: string;
70
+ selectedValues: string;
71
+ }[] = [];
72
+
73
+ if (!date) {
74
+ // This works when filters cleared
75
+ if (isFiltered(dateItem.startAccessorKey)) {
76
+ filter.push({
77
+ accessorKey: dateItem.startAccessorKey,
78
+ selectedValues: "",
79
+ });
80
+ }
81
+ if (dateItem?.endAccessorKey && isFiltered(dateItem.endAccessorKey)) {
82
+ filter.push({
83
+ accessorKey: dateItem.endAccessorKey,
84
+ selectedValues: "",
85
+ });
86
+ }
87
+
88
+ if (filter.length > 0) {
89
+ onFilterMultiple(filter);
90
+ }
91
+ return;
92
+ }
93
+
94
+ if (date instanceof Date) {
95
+ // It's a single date, no endAccessorKey
96
+ if (isFilterChanged(dateItem?.startAccessorKey, date)) {
97
+ onFilter(dateItem?.startAccessorKey, date.toISOString());
98
+ }
99
+ return;
100
+ }
101
+
102
+ if (!dateItem.canFilteredBySingleDate && (!date.start || !date.end)) return;
103
+
104
+ if (isFilterChanged(dateItem.startAccessorKey, date.start)) {
105
+ filter.push({
106
+ accessorKey: dateItem.startAccessorKey,
107
+ selectedValues: date.start?.toISOString() || "",
108
+ });
109
+ }
110
+
111
+ if (
112
+ dateItem.endAccessorKey &&
113
+ isFilterChanged(dateItem.endAccessorKey, date.end)
114
+ ) {
115
+ filter.push({
116
+ accessorKey: dateItem.endAccessorKey,
117
+ selectedValues: date.end?.toISOString() || "",
118
+ });
119
+ }
120
+
121
+ if (filter.length > 0) {
122
+ onFilterMultiple(filter);
123
+ }
124
+ }, [date]);
125
+
126
+ return (
127
+ <Popover>
128
+ <PopoverTrigger asChild>
129
+ <Button variant="outline" size="sm" className="h-8 border-dashed">
130
+ <CirclePlusIcon className="mr-2 h-4 w-4" />
131
+ {title}
132
+ {date && "start" in date && "to" in date && date.start && (
133
+ <div className="hidden space-x-1 md:flex">
134
+ <Separator orientation="vertical" className="mx-2 h-4" />
135
+ {new Date(date.start).toLocaleDateString()} -
136
+ {date.end && new Date(date?.end).toLocaleDateString()}
137
+ </div>
138
+ )}
139
+ {date instanceof Date && (
140
+ <div className="hidden space-x-1 md:flex">
141
+ <Separator orientation="vertical" className="mx-2 h-4" />
142
+ {new Date(date).toLocaleDateString()}
143
+ </div>
144
+ )}
145
+ </Button>
146
+ </PopoverTrigger>
147
+ <PopoverContent className="p-0 w-full min-w-fit" align="start">
148
+ {dateItem?.endAccessorKey ? (
149
+ <DateRangePicker
150
+ id={dateItem.startAccessorKey}
151
+ classNames={{
152
+ dateInput: "border-0 border-b rounded-none",
153
+ }}
154
+ onChange={(_date) => {
155
+ setDate(_date);
156
+ }}
157
+ defaultValues={date as DateRange}
158
+ />
159
+ ) : (
160
+ <DatePicker
161
+ id={dateItem.endAccessorKey || ""}
162
+ onChange={(_date) => {
163
+ setDate(_date);
164
+ }}
165
+ defaultValue={date as Date | undefined}
166
+ />
167
+ )}
168
+ <div className="p-1">
169
+ <Button
170
+ onClick={() => setDate(undefined)}
171
+ variant="ghost"
172
+ className="justify-center text-center w-full hover:bg-accent text-accent-foreground"
173
+ >
174
+ Clean Filter
175
+ </Button>
176
+ </div>
177
+ </PopoverContent>
178
+ </Popover>
179
+ );
180
+ }
@@ -0,0 +1,158 @@
1
+ import { CheckIcon, CirclePlusIcon } from "lucide-react";
2
+ import { Column } from "@tanstack/react-table";
3
+
4
+ import { useEffect, useState } from "react";
5
+ import { Badge } from "@repo/ayasofyazilim-ui/components/badge";
6
+ import { Button } from "@repo/ayasofyazilim-ui/components/button";
7
+ import {
8
+ Command,
9
+ CommandEmpty,
10
+ CommandGroup,
11
+ CommandInput,
12
+ CommandItem,
13
+ CommandList,
14
+ CommandSeparator,
15
+ } from "@repo/ayasofyazilim-ui/components/command";
16
+ import {
17
+ Popover,
18
+ PopoverContent,
19
+ PopoverTrigger,
20
+ } from "@repo/ayasofyazilim-ui/components/popover";
21
+ import { Separator } from "@repo/ayasofyazilim-ui/components/separator";
22
+ import { cn } from "@repo/ayasofyazilim-ui/lib/utils";
23
+ import { TanstackTableFacetedFilterType } from "../types";
24
+
25
+ interface DataTableFacetedFilterProps<TData, TValue> {
26
+ accessorKey: string;
27
+ column?: Column<TData, TValue>;
28
+ onFilter: (accessorKey: string, selectedValues: string) => void;
29
+ options: TanstackTableFacetedFilterType[];
30
+ params: URLSearchParams;
31
+ title: string;
32
+ }
33
+
34
+ export function TanstackTableFacetedFilter<TData, TValue>({
35
+ column,
36
+ accessorKey,
37
+ options,
38
+ onFilter,
39
+ params,
40
+ title,
41
+ }: DataTableFacetedFilterProps<TData, TValue>) {
42
+ const facets = column?.getFacetedUniqueValues();
43
+ const [selectedValues, setSelectedValues] = useState(
44
+ new Set(params?.get(accessorKey)?.split(",") || [])
45
+ );
46
+ useEffect(() => {
47
+ setSelectedValues(new Set(params?.get(accessorKey)?.split(",") || []));
48
+ }, [params?.get(accessorKey)]);
49
+
50
+ return (
51
+ <Popover>
52
+ <PopoverTrigger asChild>
53
+ <Button variant="outline" size="sm" className="h-8 border-dashed">
54
+ <CirclePlusIcon className="mr-2 h-4 w-4" />
55
+ {title}
56
+ {selectedValues?.size > 0 && (
57
+ <>
58
+ <Separator orientation="vertical" className="mx-2 h-4" />
59
+ <Badge
60
+ variant="secondary"
61
+ className="rounded-sm px-1 font-normal lg:hidden"
62
+ >
63
+ {selectedValues.size}
64
+ </Badge>
65
+ <div className="hidden space-x-1 lg:flex">
66
+ {selectedValues.size > 2 ? (
67
+ <Badge
68
+ variant="secondary"
69
+ className="rounded-sm px-1 font-normal"
70
+ >
71
+ {selectedValues.size} Selected
72
+ </Badge>
73
+ ) : (
74
+ options
75
+ .filter((option) => selectedValues.has(option.value))
76
+ .map((option) => (
77
+ <Badge
78
+ variant="secondary"
79
+ key={option.value}
80
+ className="rounded-sm px-1 font-normal"
81
+ >
82
+ {option.label}
83
+ </Badge>
84
+ ))
85
+ )}
86
+ </div>
87
+ </>
88
+ )}
89
+ </Button>
90
+ </PopoverTrigger>
91
+ <PopoverContent className="w-[200px] p-0" align="start">
92
+ <Command>
93
+ <CommandInput placeholder={title} />
94
+ <CommandList>
95
+ <CommandEmpty>No Filter results</CommandEmpty>
96
+ <CommandGroup>
97
+ {options.map((option) => {
98
+ const isSelected = selectedValues.has(option.value);
99
+ return (
100
+ <CommandItem
101
+ key={option.value}
102
+ onSelect={() => {
103
+ const current = Array.from(selectedValues);
104
+ if (isSelected) {
105
+ current.splice(
106
+ current.findIndex((i) => i === option.value),
107
+ 1
108
+ );
109
+ } else {
110
+ current.push(option.value);
111
+ }
112
+
113
+ onFilter(accessorKey, current.join(","));
114
+ setSelectedValues(new Set(current));
115
+ }}
116
+ >
117
+ <div
118
+ className={cn(
119
+ "mr-2 flex h-4 w-4 items-center justify-center rounded-sm border border-primary",
120
+ isSelected
121
+ ? "bg-primary text-primary-foreground"
122
+ : "opacity-50 [&_svg]:invisible"
123
+ )}
124
+ >
125
+ <CheckIcon className={cn("h-4 w-4")} />
126
+ </div>
127
+ {option.icon && (
128
+ <option.icon className="mr-2 h-4 w-4 text-muted-foreground" />
129
+ )}
130
+ <span>{option.label}</span>
131
+ {facets?.get(option.value) && (
132
+ <span className="ml-auto flex h-4 w-4 items-center justify-center font-mono text-xs">
133
+ {facets.get(option.value)}
134
+ </span>
135
+ )}
136
+ </CommandItem>
137
+ );
138
+ })}
139
+ </CommandGroup>
140
+ {selectedValues.size > 0 && (
141
+ <>
142
+ <CommandSeparator />
143
+ <CommandGroup>
144
+ <CommandItem
145
+ onSelect={() => onFilter(accessorKey, "")}
146
+ className="justify-center text-center"
147
+ >
148
+ Clean Filter
149
+ </CommandItem>
150
+ </CommandGroup>
151
+ </>
152
+ )}
153
+ </CommandList>
154
+ </Command>
155
+ </PopoverContent>
156
+ </Popover>
157
+ );
158
+ }
@@ -0,0 +1,76 @@
1
+ import { CirclePlusIcon } from "lucide-react";
2
+ import { Column } from "@tanstack/react-table";
3
+
4
+ import { useEffect, useState } from "react";
5
+ import { Button } from "@repo/ayasofyazilim-ui/components/button";
6
+ import {
7
+ Command,
8
+ CommandInput,
9
+ } from "@repo/ayasofyazilim-ui/components/command";
10
+ import {
11
+ Popover,
12
+ PopoverContent,
13
+ PopoverTrigger,
14
+ } from "@repo/ayasofyazilim-ui/components/popover";
15
+ import { Separator } from "@repo/ayasofyazilim-ui/components/separator";
16
+ import { useDebounce } from "@repo/ayasofyazilim-ui/hooks/use-debounce";
17
+
18
+ interface TanstackTableTextFilterProps<TData, TValue> {
19
+ accessorKey: string;
20
+ column?: Column<TData, TValue>;
21
+ onFilter: (accessorKey: string, selectedValues: string) => void;
22
+ params: URLSearchParams;
23
+ title?: string;
24
+ }
25
+
26
+ export function TanstackTableTextFilter<TData, TValue>({
27
+ column,
28
+ title: _title,
29
+ accessorKey,
30
+ params,
31
+ onFilter,
32
+ }: TanstackTableTextFilterProps<TData, TValue>) {
33
+ const title = _title || column?.columnDef?.meta?.toString() || accessorKey;
34
+ const [searchInput, setSearchInput] = useState("");
35
+ const filterValue = useDebounce(searchInput || "", 500);
36
+
37
+ useEffect(() => {
38
+ if (params?.get(accessorKey) !== filterValue) {
39
+ onFilter(accessorKey, filterValue);
40
+ }
41
+ }, [filterValue]);
42
+ return (
43
+ <Popover>
44
+ <PopoverTrigger asChild>
45
+ <Button variant="outline" size="sm" className="h-8 border-dashed">
46
+ <CirclePlusIcon className="mr-2 h-4 w-4" />
47
+ {title}
48
+ {filterValue?.length > 0 && (
49
+ <>
50
+ <Separator orientation="vertical" className="mx-2 h-4" />
51
+ <div className="hidden space-x-1 lg:flex">{filterValue}</div>
52
+ </>
53
+ )}
54
+ </Button>
55
+ </PopoverTrigger>
56
+ <PopoverContent className="w-[200px] p-0" align="start">
57
+ <Command>
58
+ <CommandInput
59
+ placeholder={title}
60
+ value={searchInput}
61
+ onValueChange={(value) => setSearchInput(value)}
62
+ />
63
+ </Command>
64
+ <div className="p-1">
65
+ <Button
66
+ onClick={() => setSearchInput("")}
67
+ variant="ghost"
68
+ className="justify-center text-center w-full hover:bg-accent text-accent-foreground"
69
+ >
70
+ Clean Filter
71
+ </Button>
72
+ </div>
73
+ </PopoverContent>
74
+ </Popover>
75
+ );
76
+ }
@@ -0,0 +1,136 @@
1
+ "use no memo";
2
+ import {
3
+ ChevronLeftIcon,
4
+ ChevronRightIcon,
5
+ ChevronsLeftIcon,
6
+ ChevronsRightIcon,
7
+ } from "lucide-react";
8
+ import { Table } from "@tanstack/react-table";
9
+
10
+ import { usePathname, useRouter, useSearchParams } from "next/navigation";
11
+ import { useEffect } from "react";
12
+ import { Button } from "@repo/ayasofyazilim-ui/components/button";
13
+ import {
14
+ Select,
15
+ SelectContent,
16
+ SelectItem,
17
+ SelectTrigger,
18
+ SelectValue,
19
+ } from "@repo/ayasofyazilim-ui/components/select";
20
+
21
+ interface TanstackTablePaginationProps<TData> {
22
+ pagination: {
23
+ pageIndex: number;
24
+ pageSize: number;
25
+ };
26
+ table: Table<TData>;
27
+ }
28
+
29
+ export function TanstackTablePagination<TData>(
30
+ props: TanstackTablePaginationProps<TData>
31
+ ) {
32
+ const { table, pagination } = props;
33
+ const { replace } = useRouter();
34
+ const pathname = usePathname();
35
+ const searchParams = useSearchParams();
36
+
37
+ useEffect(() => {
38
+ const params = new URLSearchParams(searchParams.toString());
39
+
40
+ if (Number(searchParams?.get("maxResultCount")) !== pagination.pageSize) {
41
+ params.set("maxResultCount", pagination.pageSize.toString());
42
+ }
43
+ if (
44
+ Number(searchParams?.get("skipCount")) !==
45
+ pagination.pageIndex * pagination.pageSize
46
+ ) {
47
+ params.set(
48
+ "skipCount",
49
+ (pagination.pageIndex * pagination.pageSize).toString()
50
+ );
51
+ }
52
+ if (Number(params?.get("maxResultCount")) === 10) {
53
+ params.delete("maxResultCount");
54
+ }
55
+ if (Number(params?.get("skipCount")) === 0) {
56
+ params.delete("skipCount");
57
+ }
58
+
59
+ replace(`${pathname}?${params.toString()}`);
60
+ }, [pagination]);
61
+
62
+ return (
63
+ <div className="flex items-center flex-wrap">
64
+ {table.getIsSomeRowsSelected() && (
65
+ <div className="text-sm text-muted-foreground text-nowrap">
66
+ {table.getFilteredSelectedRowModel().rows.length} of{" "}
67
+ {table.getFilteredRowModel().rows.length} row(s) selected.
68
+ </div>
69
+ )}
70
+ <div className="flex items-center flex-wrap gap-2 w-full sm:w-auto ml-auto">
71
+ <div className="flex items-center w-full justify-between gap-2 sm:w-auto md:mr-4">
72
+ <p className="text-sm font-medium">Rows per page</p>
73
+ <Select
74
+ value={`${table.getState().pagination.pageSize}`}
75
+ onValueChange={(value) => {
76
+ table.setPageSize(Number(value));
77
+ }}
78
+ >
79
+ <SelectTrigger className="h-8 w-[70px]">
80
+ <SelectValue placeholder={table.getState().pagination.pageSize} />
81
+ </SelectTrigger>
82
+ <SelectContent side="top">
83
+ {[10, 20, 30, 40, 50].map((pageSize) => (
84
+ <SelectItem key={pageSize} value={`${pageSize}`}>
85
+ {pageSize}
86
+ </SelectItem>
87
+ ))}
88
+ </SelectContent>
89
+ </Select>
90
+ </div>
91
+ <div className="flex items-center justify-center text-sm font-medium mr-auto">
92
+ Page {table.getState().pagination.pageIndex + 1} of{" "}
93
+ {table.getPageCount()}
94
+ </div>
95
+ <div className="flex items-center gap-x-2">
96
+ <Button
97
+ variant="outline"
98
+ className="hidden h-8 w-8 p-0 lg:flex"
99
+ onClick={() => table.setPageIndex(0)}
100
+ disabled={!table.getCanPreviousPage()}
101
+ >
102
+ <span className="sr-only">Go to first page</span>
103
+ <ChevronsLeftIcon className="h-4 w-4" />
104
+ </Button>
105
+ <Button
106
+ variant="outline"
107
+ className="h-8 w-8 p-0"
108
+ onClick={() => table.previousPage()}
109
+ disabled={!table.getCanPreviousPage()}
110
+ >
111
+ <span className="sr-only">Go to previous page</span>
112
+ <ChevronLeftIcon className="h-4 w-4" />
113
+ </Button>
114
+ <Button
115
+ variant="outline"
116
+ className="h-8 w-8 p-0"
117
+ onClick={() => table.nextPage()}
118
+ disabled={!table.getCanNextPage()}
119
+ >
120
+ <span className="sr-only">Go to next page</span>
121
+ <ChevronRightIcon className="h-4 w-4" />
122
+ </Button>
123
+ <Button
124
+ variant="outline"
125
+ className="hidden h-8 w-8 p-0 lg:flex"
126
+ onClick={() => table.setPageIndex(table.getPageCount() - 1)}
127
+ disabled={!table.getCanNextPage()}
128
+ >
129
+ <span className="sr-only">Go to last page</span>
130
+ <ChevronsRightIcon className="h-4 w-4" />
131
+ </Button>
132
+ </div>
133
+ </div>
134
+ </div>
135
+ );
136
+ }
@@ -0,0 +1,142 @@
1
+ "use no memo";
2
+ import {
3
+ ColumnDef,
4
+ flexRender,
5
+ Table as TableType,
6
+ } from "@tanstack/react-table";
7
+ import { Fragment, JSX } from "react";
8
+ import { cn } from "@repo/ayasofyazilim-ui/lib/utils";
9
+ import {
10
+ Table,
11
+ TableBody,
12
+ TableCell,
13
+ TableHead,
14
+ TableHeader,
15
+ TableRow,
16
+ } from "@repo/ayasofyazilim-ui/components/table";
17
+ import { getCommonPinningStyles } from "../utils";
18
+
19
+ export function TanstackTablePlainTable<TData, TValue>({
20
+ table,
21
+ columns,
22
+ fillerColumn,
23
+ editable,
24
+ resizeable,
25
+ expandedRowComponent,
26
+ }: {
27
+ columns: ColumnDef<TData, TValue>[];
28
+ fillerColumn?: keyof TData;
29
+ editable?: boolean;
30
+ expandedRowComponent?: (
31
+ row: TData,
32
+ toggleExpanded: () => void
33
+ ) => JSX.Element;
34
+ table: TableType<TData>;
35
+ resizeable?: boolean;
36
+ }) {
37
+ return (
38
+ <Table style={{ width: table.getCenterTotalSize(), minWidth: "100%" }}>
39
+ <TableHeader>
40
+ {table.getHeaderGroups().map((headerGroup) => (
41
+ <TableRow key={headerGroup.id} className="group">
42
+ {headerGroup.headers.map((header) => {
43
+ if (header.id === "actions") return null;
44
+ return (
45
+ <TableHead
46
+ key={header.id}
47
+ colSpan={header.colSpan}
48
+ className={cn(
49
+ " relative group/th border-r border-gray-200",
50
+ header.column.getIsResizing() &&
51
+ "border-dashed border-black border-r"
52
+ )}
53
+ style={{
54
+ ...getCommonPinningStyles({
55
+ column: header.column,
56
+ withBorder: true,
57
+ fillerColumn,
58
+ resizeable,
59
+ }),
60
+ }}
61
+ >
62
+ <div
63
+ className={cn(
64
+ header.column.getIsResizing() && "resizing",
65
+ "group-has-[.resizing]:pointer-events-none group-has-[.resizing]:select-none"
66
+ )}
67
+ >
68
+ {header.isPlaceholder
69
+ ? null
70
+ : flexRender(
71
+ header.column.columnDef.header,
72
+ header.getContext()
73
+ )}
74
+ </div>
75
+ {resizeable && (
76
+ <div
77
+ onDoubleClick={() => header.column.resetSize()}
78
+ onMouseDown={header.getResizeHandler()}
79
+ onTouchStart={header.getResizeHandler()}
80
+ role="button"
81
+ tabIndex={0}
82
+ aria-label="Resize column"
83
+ className="resizer w-0.5 hidden group-hover:flex absolute right-0 bg-muted-foreground top-0 h-10 z-10 items-center cursor-col-resize select-none touch-none"
84
+ />
85
+ )}
86
+ </TableHead>
87
+ );
88
+ })}
89
+ </TableRow>
90
+ ))}
91
+ </TableHeader>
92
+ <TableBody>
93
+ {table.getRowModel().rows?.length ? (
94
+ table.getRowModel().rows.map((row) => (
95
+ <Fragment key={row.id}>
96
+ <TableRow
97
+ data-state={row.getIsSelected() && "selected"}
98
+ className={cn(editable && "[&>td:last-child]:border-r-0")}
99
+ >
100
+ {row.getVisibleCells().map((cell) => (
101
+ <TableCell
102
+ key={cell.id}
103
+ className={cn(
104
+ (editable || cell.column.id === "actions") &&
105
+ "p-0 border border-b-0 border-r-0"
106
+ )}
107
+ style={{
108
+ ...getCommonPinningStyles({
109
+ column: cell.column,
110
+ fillerColumn,
111
+ resizeable,
112
+ withBorder: true,
113
+ }),
114
+ }}
115
+ >
116
+ {flexRender(cell.column.columnDef.cell, cell.getContext())}
117
+ </TableCell>
118
+ ))}
119
+ </TableRow>
120
+ {row.getIsExpanded() && expandedRowComponent && (
121
+ <TableRow>
122
+ <TableCell colSpan={row.getAllCells().length}>
123
+ {expandedRowComponent(
124
+ row.original,
125
+ row.getToggleExpandedHandler()
126
+ )}
127
+ </TableCell>
128
+ </TableRow>
129
+ )}
130
+ </Fragment>
131
+ ))
132
+ ) : (
133
+ <TableRow>
134
+ <TableCell colSpan={columns.length} className="h-auto text-center">
135
+ No data results
136
+ </TableCell>
137
+ </TableRow>
138
+ )}
139
+ </TableBody>
140
+ </Table>
141
+ );
142
+ }