@cryptlex/web-components 5.2.0 → 5.3.0-alpha

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 (166) hide show
  1. package/dist/components/data-table/data-table-filter.d.ts +27 -0
  2. package/dist/components/data-table/data-table-filter.js +2 -0
  3. package/dist/components/data-table/data-table-filter.js.map +1 -0
  4. package/dist/components/data-table/data-table.d.ts +72 -0
  5. package/dist/components/data-table/data-table.js +2 -0
  6. package/dist/components/data-table/data-table.js.map +1 -0
  7. package/dist/components/data-table/table-commons.d.ts +56 -0
  8. package/dist/components/data-table/table-commons.js +2 -0
  9. package/dist/components/data-table/table-commons.js.map +1 -0
  10. package/dist/components/inputs/checkbox.d.ts +8 -0
  11. package/dist/components/inputs/checkbox.js +2 -0
  12. package/dist/components/inputs/checkbox.js.map +1 -0
  13. package/dist/components/inputs/date-picker.d.ts +11 -0
  14. package/dist/components/inputs/date-picker.js +2 -0
  15. package/dist/components/inputs/date-picker.js.map +1 -0
  16. package/dist/components/inputs/datefield.d.ts +14 -0
  17. package/dist/components/inputs/datefield.js +2 -0
  18. package/dist/components/inputs/datefield.js.map +1 -0
  19. package/dist/components/inputs/field.d.ts +20 -0
  20. package/dist/components/inputs/field.js +2 -0
  21. package/dist/components/inputs/field.js.map +1 -0
  22. package/dist/components/inputs/id-search.d.ts +20 -0
  23. package/dist/components/inputs/id-search.js +2 -0
  24. package/dist/components/inputs/id-search.js.map +1 -0
  25. package/dist/components/inputs/input-otp.d.ts +7 -0
  26. package/dist/components/inputs/input-otp.js +2 -0
  27. package/dist/components/inputs/input-otp.js.map +1 -0
  28. package/dist/components/inputs/multi-select.d.ts +16 -0
  29. package/dist/components/inputs/multi-select.js +2 -0
  30. package/dist/components/inputs/multi-select.js.map +1 -0
  31. package/dist/components/inputs/numberfield.d.ts +6 -0
  32. package/dist/components/inputs/numberfield.js +2 -0
  33. package/dist/components/inputs/numberfield.js.map +1 -0
  34. package/dist/components/inputs/searchfield.d.ts +5 -0
  35. package/dist/components/inputs/searchfield.js +2 -0
  36. package/dist/components/inputs/searchfield.js.map +1 -0
  37. package/dist/components/inputs/select-options.d.ts +8 -0
  38. package/dist/components/inputs/select-options.js +2 -0
  39. package/dist/components/inputs/select-options.js.map +1 -0
  40. package/dist/components/inputs/select.d.ts +17 -0
  41. package/dist/components/inputs/select.js +2 -0
  42. package/dist/components/inputs/select.js.map +1 -0
  43. package/dist/components/inputs/textfield.d.ts +7 -0
  44. package/dist/components/inputs/textfield.js +2 -0
  45. package/dist/components/inputs/textfield.js.map +1 -0
  46. package/dist/components/key-value-card/key-value-card.d.ts +17 -0
  47. package/dist/components/key-value-card/key-value-card.js +2 -0
  48. package/dist/components/key-value-card/key-value-card.js.map +1 -0
  49. package/dist/components/ui/alert.d.ts +8 -0
  50. package/dist/components/ui/alert.js +2 -0
  51. package/dist/components/ui/alert.js.map +1 -0
  52. package/dist/components/ui/avatar.d.ts +7 -0
  53. package/dist/components/ui/avatar.js +2 -0
  54. package/dist/components/ui/avatar.js.map +1 -0
  55. package/dist/components/ui/badge.d.ts +1 -0
  56. package/dist/components/ui/badge.js +2 -0
  57. package/dist/components/ui/badge.js.map +1 -0
  58. package/dist/components/ui/breadcrumbs.d.ts +10 -0
  59. package/dist/components/ui/breadcrumbs.js +2 -0
  60. package/dist/components/ui/breadcrumbs.js.map +1 -0
  61. package/dist/components/ui/button.d.ts +11 -0
  62. package/dist/components/ui/button.js +2 -0
  63. package/dist/components/ui/button.js.map +1 -0
  64. package/dist/components/ui/calendar.d.ts +16 -0
  65. package/dist/components/ui/calendar.js +2 -0
  66. package/dist/components/ui/calendar.js.map +1 -0
  67. package/dist/components/ui/card.d.ts +7 -0
  68. package/dist/components/ui/card.js +2 -0
  69. package/dist/components/ui/card.js.map +1 -0
  70. package/dist/components/ui/dialog.d.ts +18 -0
  71. package/dist/components/ui/dialog.js +2 -0
  72. package/dist/components/ui/dialog.js.map +1 -0
  73. package/dist/components/ui/disclosure.d.ts +18 -0
  74. package/dist/components/ui/disclosure.js +2 -0
  75. package/dist/components/ui/disclosure.js.map +1 -0
  76. package/dist/components/ui/list-box.d.ts +5 -0
  77. package/dist/components/ui/list-box.js +2 -0
  78. package/dist/components/ui/list-box.js.map +1 -0
  79. package/dist/components/ui/loader.d.ts +5 -0
  80. package/dist/components/ui/loader.js +2 -0
  81. package/dist/components/ui/loader.js.map +1 -0
  82. package/dist/components/ui/menu.d.ts +24 -0
  83. package/dist/components/ui/menu.js +2 -0
  84. package/dist/components/ui/menu.js.map +1 -0
  85. package/dist/components/ui/popover.d.ts +4 -0
  86. package/dist/components/ui/popover.js +2 -0
  87. package/dist/components/ui/popover.js.map +1 -0
  88. package/dist/components/ui/sidebar.d.ts +52 -0
  89. package/dist/components/ui/sidebar.js +2 -0
  90. package/dist/components/ui/sidebar.js.map +1 -0
  91. package/dist/components/ui/skeleton.d.ts +1 -0
  92. package/dist/components/ui/skeleton.js +2 -0
  93. package/dist/components/ui/skeleton.js.map +1 -0
  94. package/dist/components/ui/sonner.d.ts +4 -0
  95. package/dist/components/ui/sonner.js +2 -0
  96. package/dist/components/ui/sonner.js.map +1 -0
  97. package/dist/components/ui/table.d.ts +8 -0
  98. package/dist/components/ui/table.js +2 -0
  99. package/dist/components/ui/table.js.map +1 -0
  100. package/dist/components/ui/tabs.d.ts +5 -0
  101. package/dist/components/ui/tabs.js +2 -0
  102. package/dist/components/ui/tabs.js.map +1 -0
  103. package/dist/components/ui/timeline.d.ts +15 -0
  104. package/dist/components/ui/timeline.js +2 -0
  105. package/dist/components/ui/timeline.js.map +1 -0
  106. package/dist/components/ui/tooltip.d.ts +4 -0
  107. package/dist/components/ui/tooltip.js +2 -0
  108. package/dist/components/ui/tooltip.js.map +1 -0
  109. package/dist/utils/form-context.d.ts +4 -0
  110. package/dist/utils/form-context.js +2 -0
  111. package/dist/utils/form-context.js.map +1 -0
  112. package/dist/utils/form-hook.d.ts +23 -0
  113. package/dist/utils/form-hook.js +2 -0
  114. package/dist/utils/form-hook.js.map +1 -0
  115. package/dist/utils/primitives.d.ts +44 -0
  116. package/dist/utils/primitives.js +2 -0
  117. package/dist/utils/primitives.js.map +1 -0
  118. package/dist/utils/resource-names.d.ts +11 -0
  119. package/dist/utils/resource-names.js +2 -0
  120. package/dist/utils/resource-names.js.map +1 -0
  121. package/dist/utils/use-mobile.d.ts +1 -0
  122. package/dist/utils/use-mobile.js +2 -0
  123. package/dist/utils/use-mobile.js.map +1 -0
  124. package/package.json +16 -8
  125. package/lib/components/data-table/data-table-filter.tsx +0 -220
  126. package/lib/components/data-table/data-table.tsx +0 -593
  127. package/lib/components/data-table/table-commons.tsx +0 -233
  128. package/lib/components/inputs/checkbox.tsx +0 -72
  129. package/lib/components/inputs/date-picker.tsx +0 -130
  130. package/lib/components/inputs/datefield.tsx +0 -109
  131. package/lib/components/inputs/field.tsx +0 -106
  132. package/lib/components/inputs/id-search.tsx +0 -83
  133. package/lib/components/inputs/input-otp.tsx +0 -63
  134. package/lib/components/inputs/multi-select.tsx +0 -62
  135. package/lib/components/inputs/numberfield.tsx +0 -110
  136. package/lib/components/inputs/searchfield.tsx +0 -87
  137. package/lib/components/inputs/select-options.tsx +0 -303
  138. package/lib/components/inputs/select.tsx +0 -140
  139. package/lib/components/inputs/textfield.tsx +0 -96
  140. package/lib/components/key-value-card/key-value-card.tsx +0 -115
  141. package/lib/components/ui/alert.tsx +0 -32
  142. package/lib/components/ui/avatar.tsx +0 -22
  143. package/lib/components/ui/badge.tsx +0 -19
  144. package/lib/components/ui/breadcrumbs.tsx +0 -104
  145. package/lib/components/ui/button.tsx +0 -66
  146. package/lib/components/ui/calendar.tsx +0 -220
  147. package/lib/components/ui/card.tsx +0 -58
  148. package/lib/components/ui/dialog.tsx +0 -172
  149. package/lib/components/ui/disclosure.tsx +0 -113
  150. package/lib/components/ui/list-box.tsx +0 -86
  151. package/lib/components/ui/loader.tsx +0 -10
  152. package/lib/components/ui/menu.tsx +0 -168
  153. package/lib/components/ui/popover.tsx +0 -37
  154. package/lib/components/ui/sidebar.tsx +0 -552
  155. package/lib/components/ui/skeleton.tsx +0 -7
  156. package/lib/components/ui/sonner.tsx +0 -26
  157. package/lib/components/ui/table.tsx +0 -79
  158. package/lib/components/ui/tabs.tsx +0 -82
  159. package/lib/components/ui/timeline.tsx +0 -52
  160. package/lib/components/ui/tooltip.tsx +0 -30
  161. package/lib/tokens.scss +0 -89
  162. package/lib/utils/form-context.tsx +0 -7
  163. package/lib/utils/form-hook.tsx +0 -33
  164. package/lib/utils/primitives.ts +0 -68
  165. package/lib/utils/resource-names.tsx +0 -245
  166. package/lib/utils/use-mobile.tsx +0 -21
@@ -1,593 +0,0 @@
1
- "use client";
2
- import type { components, operations } from "@cryptlex/web-api-types";
3
- import { keepPreviousData, useQuery } from "@tanstack/react-query";
4
- import {
5
- type AccessorFnColumnDef,
6
- type Column,
7
- type ColumnDef,
8
- type ColumnOrderState,
9
- createColumnHelper,
10
- getCoreRowModel,
11
- type TableState,
12
- type Updater,
13
- useReactTable,
14
- type VisibilityState
15
- } from "@tanstack/react-table";
16
- import { ArrowDownNarrowWide, ArrowDownWideNarrow, ArrowUpDown, Columns3, GripVertical, Info } from "lucide-react";
17
- import React, { createContext, useContext, useEffect, useId, useMemo, useState } from "react";
18
-
19
- /** Reserved name for actions column */
20
- export const ACTIONS_COLUMN_ID = "tableActions";
21
-
22
- export type Schemas = ApiSchema<keyof components['schemas']>;
23
- export type OperationKeys = keyof operations;
24
-
25
- type DataTableFactory<TData extends Schemas> = {
26
- fetchFn: TableFetchFn<TData, OperationKeys>;
27
- columns: ColumnDef<TData, any>[]
28
- allowSelection?: boolean;
29
- columnsToHideByDefault?: VisibilityState; // Columns that are hidden by default
30
- filterConfig: FiltersConfig<OperationKeys>
31
- }
32
-
33
-
34
- type DataTableState = Pick<TableState, 'sorting' | 'rowSelection' | 'pagination'> & {};
35
- /**
36
- * Hook for handling all data-table state. Used in DataTableContext
37
- */
38
- export function useDataTableState<TData extends Schemas>({ columns, fetchFn, columnsToHideByDefault = {}, allowSelection = false, filterConfig }: DataTableFactory<TData>) {
39
- const id = useId();
40
-
41
- // TODO: Would it be better for this state to be more granular?
42
- const [tableState, _setTableState] = useState<DataTableState>({
43
- /** TODO Reflect in URL */
44
- pagination: { pageIndex: 0, pageSize: 20 }, // Pagination state
45
- sorting: [], // Sorting state
46
-
47
- /** Ephemeral */
48
- rowSelection: {}, // Row selection state
49
- });
50
-
51
- /** TODO Reflect in URL */
52
- const [searchQuery, setSearchQuery] = useState('');
53
-
54
- /** TODO Store on browser as preference */
55
- const [columnOrder, setColumnOrder] = useState<ColumnOrderState>([])
56
- // TODO Store on browser
57
- const [columnVisibility, setColumnVisibility] = useState<VisibilityState>({
58
- id: false,
59
- updatedAt: false,
60
- ...columnsToHideByDefault,
61
- });
62
-
63
- const [filters, setFilters] = useState<ApiFilters<OperationKeys>[]>([]);
64
-
65
- const mergedFilters = useMemo(() => {
66
- return filters.reduce((acc, current) => {
67
- return merge(acc, current);
68
- }, {});
69
- }, [filters])
70
-
71
- // Update table state with new values
72
- const updateTableState = (updates: Partial<DataTableState>) => {
73
- _setTableState((prev) => ({ ...prev, ...updates }));
74
- };
75
-
76
- const {
77
- sorting,
78
- rowSelection,
79
- pagination,
80
- } = tableState;
81
-
82
- const query = useQuery({
83
- queryKey: [id, pagination, sorting, searchQuery],
84
- queryFn: () => fetchFn(pagination, sorting, searchQuery, mergedFilters),
85
- placeholderData: keepPreviousData, // Keep previous data while loading new data
86
- retry: 0,
87
- refetchOnWindowFocus: false,
88
- });
89
-
90
- useEffect(() => {
91
- // TODO, store in localStorage
92
- }, [columnVisibility])
93
-
94
- const columnHelper = useMemo(() => createColumnHelper<TData>(), []);
95
-
96
- const metadataColumns = useMemo<AccessorFnColumnDef<TData, string>[]>(() => {
97
- const data = query.data?.data;
98
- if (!data?.length) return [];
99
-
100
- type WithMeta = TData & { metadata: ApiSchema<'MetadataDto'>[] };
101
- const rowHasMetadata = (row: TData): row is WithMeta => row != null &&
102
- typeof row === 'object' &&
103
- 'metadata' in row &&
104
- Array.isArray((row).metadata);
105
-
106
- const rowsWithMeta = data.filter(
107
- rowHasMetadata
108
- );
109
- if (rowsWithMeta.length === 0) return [];
110
- const keys = Array.from(new Set(rowsWithMeta.flatMap(r => r.metadata?.map(m => m.key) ?? [])));
111
-
112
- return keys.map(key =>
113
- columnHelper.accessor(
114
- (row) => {
115
- if (rowHasMetadata(row)) {
116
- return row?.metadata?.find(m => m.key === key)?.value ?? '';
117
- }
118
- return '';
119
- },
120
- {
121
- id: key,
122
- header: key, // tooltip header
123
- enableSorting: false,
124
- cell: (info) => {
125
- const value = info.getValue();
126
- // Handle null/undefined values
127
- if (value === null || value === undefined) return "";
128
- // For primitive types, return the string representation
129
- return String(value);
130
- },
131
- }
132
- ));
133
- }, [query.data?.data]);
134
-
135
- /**
136
- * ID,createdAt and updatedAt will be added by default for all tables
137
- * If selection is allowed, checkbox will be added
138
- * If the dto has metadata, dynamics columns for all the metadata key-value will be added(particular for a view)
139
- * If there are actions for the table, they will be placed fixed at the right side of table.
140
- */
141
- const cols: ColumnDef<any, any>[] = [
142
- ...(allowSelection ? TABLE_CHECK_BOX_COLUMN : []),
143
- ...TABLE_ID_COLUMN,
144
- ...columns.filter((col) => col.id !== ACTIONS_COLUMN_ID),
145
- ...(metadataColumns.length ? metadataColumns : []),
146
- ...TABLE_DEFAULT_DATE_COLUMNS,
147
- // Actions column
148
- ...columns.filter((col) => col.id === ACTIONS_COLUMN_ID),
149
- ];
150
-
151
- // Type-guard for updater
152
- function isUpdaterFunction<T>(updater: Updater<T>): updater is (old: T) => T {
153
- return typeof updater === "function";
154
- }
155
- // Utility function to resolve updater
156
- function resolveUpdater<T>(updater: Updater<T>, currentValue: T) {
157
- if (isUpdaterFunction(updater)) {
158
- return updater(currentValue);
159
- }
160
- return updater;
161
- }
162
-
163
- // Use react-table's hook to create the table instance
164
- const tanTable = useReactTable({
165
- data: query.data?.data ?? [],
166
- columns: cols,
167
- getCoreRowModel: getCoreRowModel(),
168
- rowCount: query.data?.total,
169
- manualPagination: true, // Handle pagination manually since pagination is done server side for data tables
170
- onPaginationChange: (updater) => {
171
- updateTableState({ pagination: resolveUpdater(updater, pagination) });
172
- },
173
- manualSorting: true, // Handle sorting manually since sorting is done server side for data tables
174
- onSortingChange: (updater) => {
175
- updateTableState({ sorting: [...resolveUpdater(updater, sorting)], rowSelection: {} }); // Reset selection when sorting.
176
- },
177
- manualFiltering: true, // Handle filtering manually since filtering is done server side for data tables
178
- onColumnVisibilityChange: (updater) => {
179
- setColumnVisibility(resolveUpdater(updater, columnVisibility));
180
- },
181
- onRowSelectionChange: (updater) => {
182
- updateTableState({ rowSelection: resolveUpdater(updater, rowSelection) });
183
- },
184
- onColumnOrderChange: (updater) => {
185
- setColumnOrder(resolveUpdater(updater, columnOrder));
186
- },
187
- state: {
188
- sorting: sorting,
189
- columnVisibility: columnVisibility,
190
- pagination: pagination,
191
- rowSelection: rowSelection,
192
- columnOrder: columnOrder
193
- },
194
- meta: {
195
- refetch: query.refetch,
196
- },
197
- });
198
-
199
- // By default, ColumnDef does not give guarantees of column.id existing. Once useReactTable is called, all columns are assigned IDs.
200
- // This populates the columnIds in the columnOrder state
201
- // TODO, add localStorage access layer for this.
202
- useEffect(() => {
203
- setColumnOrder([...tanTable.getAllLeafColumns().map(c => c.id)]);
204
- }, [])
205
-
206
- return { tableState, updateTableState, query, setSearchQuery, searchQuery, tanTable, mergedFilters, filters, setFilters, filterConfig }
207
- }
208
-
209
- export const DataTableContext = createContext<ReturnType<typeof useDataTableState> | null>(null);
210
-
211
- export const useDataTable = () => {
212
- const ctx = useContext(DataTableContext);
213
- if (!ctx) {
214
- throw Error("DataTable should be used within DataTableProvider.")
215
- }
216
- return ctx;
217
- }
218
-
219
- export function DataTableProvider({ children, ...props }: { children: React.ReactNode; } & ReturnType<typeof useDataTableState>) {
220
- return (
221
- <DataTableContext.Provider value={props}>
222
- {children}
223
- </DataTableContext.Provider>
224
- )
225
- }
226
-
227
-
228
- export type DataTableProps = React.ComponentProps<'section'> & {
229
- tableActions: TableActions;
230
-
231
- // filterConfig: {
232
- // filters: Record<keyof ApiFilters<TOperation>, 'date' | 'string' | 'known-string' | 'number'>,
233
- // }
234
- }
235
- export function DataTable({
236
- tableActions,
237
- className,
238
- ...props
239
- // filterableFields,
240
- }: DataTableProps) {
241
- // State for managing table data and filters
242
- const { query, tanTable } = useDataTable();
243
-
244
- return (
245
- <>
246
- {/* Table Actions Section */}
247
- <section {...props} className={cn("flex flex-col bg-card", className)}>
248
- <Actions tableActions={tableActions} />
249
- {/* The div here is necessary because TableContent is internally a <table> tag and does not respect width, height CSS */}
250
- <div className="w-full overflow-auto border-x grow min-h-table relative" tabIndex={0}>
251
- {/* Table overlay with loader */}
252
- {query.isLoading && (
253
- <TableOverlay className="cursor-wait">
254
- <Loader />
255
- </TableOverlay>
256
- )}
257
- {/* Table overlay for empty table */}
258
- {!query.isLoading && tanTable.getRowModel().rows.length === 0 && (
259
- // Empty table
260
- <TableOverlay className="cursor-not-allowed">
261
- {!query.isFetching &&
262
- (query.isError ? (
263
- <span className="flex gap-3 justify-center items-center">
264
- {/* TODO (mudasir-pandith) Check for 403 explicitly!! */}
265
- {/* <span>{query.error}</span> */}
266
- <Info />
267
- <span>
268
- You don't have the required permissions. Please contact your
269
- admin.
270
- </span>
271
- </span>
272
- ) : !query.data?.data ? (
273
- <>No results found.</>
274
- ) : (
275
- <>Unknown error. Please contact customer support.</>
276
- ))}
277
- </TableOverlay>
278
- )}
279
- {!query.isLoading && tanTable.getRowModel().rows.length !== 0 &&
280
- <TableContent className="size-full" />}
281
- </div>
282
-
283
- {/* Table Footer Section with Pagination and Column Picker */}
284
- <div className="flex w-full justify-between border gap-icon p-icon overflow-x-auto">
285
- <div className="flex gap-icon">
286
- <ColumnPicker />
287
- <PageSize />
288
- </div>
289
- <Paginator />
290
- </div>
291
- </section>
292
- </>
293
- );
294
- }
295
- /** Table overlay to be shown for loaders or other messages */
296
- function TableOverlay({
297
- children,
298
- className,
299
- }: { children: React.ReactNode; className?: string }) {
300
- return (
301
- <>
302
- <span
303
- className={cn(
304
- className,
305
- "absolute top-0 bg-card z-20 size-full text-sm flex items-center justify-center",
306
- )}
307
- >
308
- {children}
309
- </span>
310
- {/* Keep something in document flow with the correct height */}
311
- <span className="relative h-full w-0 block" />
312
- </>
313
- );
314
- }
315
-
316
- import {
317
- arrayMove,
318
- SortableContext,
319
- sortableKeyboardCoordinates,
320
- useSortable,
321
- verticalListSortingStrategy
322
- } from '@dnd-kit/sortable';
323
-
324
- function ColumnPicker() {
325
- const { tanTable } = useDataTable();
326
- const [activeId, setActiveId] = useState<string | null>(null);
327
-
328
- const resourceFormatter = useResourceFormatter();
329
-
330
- const sensors = useSensors(
331
- useSensor(PointerSensor),
332
- useSensor(KeyboardSensor, {
333
- coordinateGetter: sortableKeyboardCoordinates,
334
- })
335
- );
336
- return (
337
- <DndContext
338
- sensors={sensors}
339
- collisionDetection={closestCenter}
340
- onDragStart={(event) => {
341
- const { active } = event;
342
- setActiveId(active.id.toString());
343
- }}
344
- onDragEnd={(event) => {
345
- const { active, over } = event;
346
-
347
- if (over && active.id !== over.id) {
348
- const columnOrder = tanTable.getState().columnOrder;
349
- const oldIndex = columnOrder.indexOf(active.id.toString());
350
- const newIndex = columnOrder.indexOf(over.id.toString());
351
- tanTable.setColumnOrder([...arrayMove(columnOrder, oldIndex, newIndex)]);
352
- }
353
-
354
- setActiveId(null);
355
- }}
356
- >
357
- <SortableContext
358
- items={tanTable.getState().columnOrder}
359
- strategy={verticalListSortingStrategy}
360
- >
361
- <EasyMenu label={<><Columns3 />Columns</>} selectionMode="multiple" items={tanTable.getAllFlatColumns()} selectedKeys={tanTable.getIsAllColumnsVisible() ? 'all' : tanTable.getVisibleFlatColumns().map(c => c.id)}>
362
- <MenuItem onAction={() => tanTable.toggleAllColumnsVisible()} className={'italic'}>(select all)</MenuItem>
363
- {tanTable.getState().columnOrder.map(colId => {
364
- const col = tanTable.getAllFlatColumns().find(c => c.id === colId);
365
- if (!col) return null;
366
- return <SortableItem key={col.id} column={col} />
367
- })}
368
- </EasyMenu>
369
- <DragOverlay>
370
- {activeId ? <div className="dropdown-item opacity-70 border-2 border-primary">{resourceFormatter(activeId)}</div> : null}
371
- </DragOverlay>
372
- </SortableContext>
373
- </DndContext>
374
- );
375
-
376
-
377
- function SortableItem({ column }: { column: Column<any, unknown> }) {
378
- const {
379
- attributes,
380
- listeners,
381
- setNodeRef,
382
- transform,
383
- transition,
384
- } = useSortable({ id: column.id });
385
- const style = {
386
- transform: CSS.Transform.toString(transform),
387
- transition,
388
- zIndex: '999'
389
- };
390
- return <MenuItem
391
- ref={setNodeRef}
392
- style={style}
393
- {...attributes}
394
- id={column.id}
395
- onAction={() => column.toggleVisibility()}
396
- isDisabled={!column.getCanHide()} className="flex items-center">
397
- <GripVertical {...listeners} className="size-icon cursor-grab" />
398
- {resourceFormatter(column.id)}
399
- {column.getIsSorted() && <SortIcon className="size-icon" direction={column.getIsSorted()} />}
400
- </MenuItem>;
401
- }
402
- }
403
-
404
-
405
-
406
- import {
407
- ChevronFirst,
408
- ChevronLast,
409
- ChevronLeft,
410
- ChevronRight,
411
- } from "lucide-react";
412
-
413
- function Paginator() {
414
- const { tanTable, query } = useDataTable();
415
- const rowCount = query.data?.total ?? 0
416
- return (
417
- <div className="flex items-center gap-2">
418
- <span className="whitespace-nowrap caption text-muted">
419
- {`${tanTable.getState().pagination.pageIndex * tanTable.getState().pagination.pageSize + 1} - ${Math.min(
420
- (tanTable.getState().pagination.pageIndex + 1) *
421
- tanTable.getState().pagination.pageSize,
422
- rowCount,
423
- )} of ${rowCount?.toLocaleString()}`}
424
- </span>
425
-
426
- <Button
427
- onPress={() => tanTable.firstPage()}
428
- isDisabled={!tanTable.getCanPreviousPage()}
429
- variant="neutral"
430
- size={"icon"}
431
- ><ChevronFirst /></Button>
432
- <Button
433
- onPress={() => tanTable.previousPage()}
434
- isDisabled={!tanTable.getCanPreviousPage()}
435
- variant="neutral"
436
- size={"icon"}
437
- ><ChevronLeft /></Button>
438
- <Button
439
- onPress={() => tanTable.nextPage()}
440
- isDisabled={!tanTable.getCanNextPage()}
441
- variant="neutral"
442
- size={"icon"}
443
- ><ChevronRight /></Button>
444
- <Button
445
- onClick={() => tanTable.lastPage()}
446
- isDisabled={!tanTable.getCanNextPage()}
447
- variant="neutral"
448
- size={"icon"}
449
- ><ChevronLast /></Button>
450
- </div>
451
- );
452
- }
453
-
454
-
455
- function PageSize() {
456
- const { tanTable } = useDataTable();
457
- const PAGE_SIZES = [10, 20, 30, 40, 50];
458
-
459
- return (
460
- <EasyMenu label={tanTable.getState().pagination.pageSize.toString()} selectionMode="single" selectedKeys={[tanTable.getState().pagination.pageSize.toString()]} items={PAGE_SIZES.map(s => ({ id: s.toString(), value: s }))}>
461
- {
462
- (items) => <MenuItem onAction={() => tanTable.setPageSize(items.value)}>{items.value}</MenuItem>
463
- }
464
- </EasyMenu>
465
- );
466
- }
467
-
468
-
469
- import { RotateCw } from "lucide-react";
470
-
471
- function Actions({ tableActions }: { tableActions: TableActions }) {
472
- const { query, tanTable, searchQuery, setSearchQuery } = useDataTable();
473
-
474
- return (
475
- <section className="flex bg-card justify-between my-0 p-icon border gap-icon overflow-auto">
476
- <div className="flex gap-icon">
477
- <Button
478
- isPending={query.isFetching}
479
- onClick={() => query.refetch()}
480
- variant={"neutral"}
481
- size={"icon"}
482
- ><RotateCw /></Button>
483
-
484
- {tableActions
485
- .filter(ta => ta.bulk === tanTable.getSelectedRowModel().rows.length > 0)
486
- .map((ta, i) => {
487
- const Icon = ta.icon;
488
- return (<Button key={`${i}-${ta.bulk}`} type="button" isDisabled={query.isFetching} className="animate-in fade-in slide-in-from-left-15 duration-300 transition-transform" onPress={(e) => { ta.onClick(e, tanTable) }} size={'icon'}><Icon /></Button>)
489
- }
490
- )}
491
- </div>
492
- <div className="flex gap-icon">
493
- <DataTableFilter />
494
- {(
495
- <SearchField value={searchQuery} onChange={setSearchQuery} />
496
- )}
497
- </div>
498
- </section>
499
- );
500
- }
501
-
502
-
503
- import { closestCenter, DndContext, DragOverlay, KeyboardSensor, PointerSensor, useSensor, useSensors } from "@dnd-kit/core";
504
- import { CSS } from "@dnd-kit/utilities";
505
- import {
506
- flexRender
507
- } from "@tanstack/react-table";
508
- import { DataTableFilter, type FiltersConfig } from "lib/components/data-table/data-table-filter";
509
- import { type ApiFilters, type ApiSchema, TABLE_CHECK_BOX_COLUMN, TABLE_DEFAULT_DATE_COLUMNS, TABLE_ID_COLUMN, type TableActions, type TableFetchFn } from "lib/components/data-table/table-commons";
510
- import { SearchField } from "lib/components/inputs/searchfield";
511
- import { Button } from "lib/components/ui/button";
512
- import { Loader } from "lib/components/ui/loader";
513
- import { EasyMenu, MenuItem } from "lib/components/ui/menu";
514
- import {
515
- TableBody,
516
- TableCell,
517
- Table as TableComponent,
518
- TableHead,
519
- TableHeader,
520
- TableRow,
521
- } from "lib/components/ui/table";
522
- import { cn } from "lib/utils/primitives";
523
- import { useResourceFormatter } from "lib/utils/resource-names";
524
- import { merge } from "lodash-es";
525
-
526
- function SortIcon({ direction, ...props }: { direction: 'asc' | 'desc' | false } & Omit<React.ComponentProps<'svg'>, 'direction'>) {
527
- if (direction === 'asc') return <ArrowDownNarrowWide {...props} />
528
- else if (direction === 'desc') return <ArrowDownWideNarrow {...props} />
529
- else return <ArrowUpDown {...props} />
530
- }
531
-
532
- // TODO, automate checking valid HTML
533
- function TableContent({ className }: React.ComponentProps<typeof TableComponent>) {
534
- const { tanTable } = useDataTable();
535
- const tableCellStyle = (isSticky: boolean, className?: string) => cn("animate-in fade-in slide-in-from-top-10", "px-icon py-2 text-left text-sm font-medium whitespace-nowrap", isSticky && "bg-card sticky right-0 z-50 text-center", className)
536
- return (
537
- <TableComponent className={cn(className)}>
538
- <TableHeader className="sticky top-0 z-10">
539
- {tanTable.getHeaderGroups().map((headerGroup) => (
540
- <TableRow className={cn("h-input")} key={headerGroup.id}>
541
- {headerGroup.headers.map((header) => (
542
- <TableHead
543
- key={header.id}
544
- className={tableCellStyle(false, "bg-card")}
545
- >
546
- {!header.column.getCanSort() && !header.isPlaceholder
547
- && <>{flexRender(
548
- header.column.columnDef.header,
549
- header.getContext(),
550
- )}</>}
551
- {/* TODO Align header text with table text */}
552
- {header.column.getCanSort() && (
553
- <Button
554
- variant="ghost"
555
- className="w-full !justify-start !px-1.5"
556
- onPress={header.column.getToggleSortingHandler()}
557
- >
558
- {flexRender(
559
- header.column.columnDef.header,
560
- header.getContext(),
561
- )}
562
- <SortIcon direction={header.column.getIsSorted()} />
563
- </Button>
564
- )}
565
- </TableHead>
566
- ))}
567
- </TableRow>
568
- ))}
569
- </TableHeader>
570
- <TableBody className="flex-1 overflow-y-auto relative">
571
- {tanTable.getRowModel().rows.map((row) => (
572
- <TableRow
573
- className={cn("h-input transition-colors data-[selected=true]:bg-primary/10 hover:bg-muted-foreground/20")}
574
- key={row.id}
575
- data-selected={row.getIsSelected()}
576
- >
577
- {row.getVisibleCells().map((cell) => (
578
- <TableCell
579
- key={cell.id}
580
- className={tableCellStyle(cell.column.id === ACTIONS_COLUMN_ID)}
581
- >
582
- {flexRender(
583
- cell.column.columnDef.cell,
584
- cell.getContext(),
585
- )}
586
- </TableCell>
587
- ))}
588
- </TableRow>
589
- ))}
590
- </TableBody>
591
- </TableComponent>
592
- );
593
- }