@postxl/generators 1.1.1 → 1.2.1

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 (181) hide show
  1. package/dist/backend-core/backend.generator.js +4 -1
  2. package/dist/backend-core/backend.generator.js.map +1 -1
  3. package/dist/backend-core/generators/jest.config.generator.d.ts +2 -0
  4. package/dist/backend-core/{template/jest.config.ts → generators/jest.config.generator.js} +12 -8
  5. package/dist/backend-core/generators/jest.config.generator.js.map +1 -0
  6. package/dist/backend-core/generators/tsconfig.generator.d.ts +1 -1
  7. package/dist/backend-core/generators/tsconfig.generator.js +1 -9
  8. package/dist/backend-core/generators/tsconfig.generator.js.map +1 -1
  9. package/dist/backend-core/types.d.ts +2 -0
  10. package/dist/base/base.generator.js +12 -8
  11. package/dist/base/base.generator.js.map +1 -1
  12. package/dist/e2e/template/e2e/package.json +1 -1
  13. package/dist/e2e/template/e2e/specs/example.spec.ts +1 -1
  14. package/dist/e2e/template/e2e/specs/example.spec.ts-snapshots/Navigate-to-homepage-and-take-snapshot-1-chromium-linux.png +0 -0
  15. package/dist/frontend-core/frontend.generator.d.ts +0 -58
  16. package/dist/frontend-core/frontend.generator.js +9 -173
  17. package/dist/frontend-core/frontend.generator.js.map +1 -1
  18. package/dist/frontend-core/generators/tsconfig.generator.js +2 -0
  19. package/dist/frontend-core/generators/tsconfig.generator.js.map +1 -1
  20. package/dist/frontend-core/template/README.md +1 -1
  21. package/dist/frontend-core/template/src/components/admin/table-filter.tsx +1 -5
  22. package/dist/frontend-core/template/src/components/ui/color-mode-toggle/color-mode-toggle.tsx +10 -4
  23. package/dist/frontend-core/template/src/lib/query-client.ts +45 -4
  24. package/dist/frontend-core/template/src/pages/dashboard/dashboard.page.tsx +2 -3
  25. package/dist/frontend-core/template/src/pages/error/default-error.page.tsx +45 -12
  26. package/dist/frontend-core/template/src/pages/error/not-found-error.page.tsx +1 -1
  27. package/dist/frontend-core/template/src/styles/styles.css +13 -1
  28. package/dist/frontend-core/template/tsconfig.json +2 -0
  29. package/dist/frontend-core/types/component.d.ts +1 -1
  30. package/dist/frontend-forms/generators/discriminated-union/fields.generator.js +4 -6
  31. package/dist/frontend-forms/generators/discriminated-union/fields.generator.js.map +1 -1
  32. package/dist/frontend-forms/generators/discriminated-union/inputs.generator.js +1 -1
  33. package/dist/frontend-forms/generators/discriminated-union/inputs.generator.js.map +1 -1
  34. package/dist/frontend-forms/generators/enum/inputs.generator.js +1 -1
  35. package/dist/frontend-forms/generators/enum/inputs.generator.js.map +1 -1
  36. package/dist/frontend-forms/generators/model/forms.generator.js +8 -12
  37. package/dist/frontend-forms/generators/model/forms.generator.js.map +1 -1
  38. package/dist/frontend-forms/generators/model/inputs.generator.js +2 -6
  39. package/dist/frontend-forms/generators/model/inputs.generator.js.map +1 -1
  40. package/dist/frontend-forms/template/src/components/ui/field/field.tsx +1 -4
  41. package/dist/frontend-tables/generators/model-table.generator.js +1 -5
  42. package/dist/frontend-tables/generators/model-table.generator.js.map +1 -1
  43. package/package.json +4 -3
  44. package/dist/e2e/generators/package-json.generator.d.ts +0 -2
  45. package/dist/e2e/generators/package-json.generator.js +0 -29
  46. package/dist/e2e/generators/package-json.generator.js.map +0 -1
  47. package/dist/frontend-core/template/src/components/ui/accordion/accordion.stories.tsx +0 -47
  48. package/dist/frontend-core/template/src/components/ui/accordion/accordion.tsx +0 -52
  49. package/dist/frontend-core/template/src/components/ui/admin-sidebar/admin-sidebar.tsx +0 -195
  50. package/dist/frontend-core/template/src/components/ui/alert/alert.stories.tsx +0 -61
  51. package/dist/frontend-core/template/src/components/ui/alert/alert.tsx +0 -45
  52. package/dist/frontend-core/template/src/components/ui/alert-dialog/alert-dialog.stories.tsx +0 -52
  53. package/dist/frontend-core/template/src/components/ui/alert-dialog/alert-dialog.tsx +0 -105
  54. package/dist/frontend-core/template/src/components/ui/avatar/avatar.stories.tsx +0 -30
  55. package/dist/frontend-core/template/src/components/ui/avatar/avatar.tsx +0 -39
  56. package/dist/frontend-core/template/src/components/ui/badge/badge.stories.tsx +0 -78
  57. package/dist/frontend-core/template/src/components/ui/badge/badge.tsx +0 -48
  58. package/dist/frontend-core/template/src/components/ui/breadcrumb/breadcrumb.stories.tsx +0 -67
  59. package/dist/frontend-core/template/src/components/ui/breadcrumb/breadcrumb.tsx +0 -85
  60. package/dist/frontend-core/template/src/components/ui/button/button.stories.tsx +0 -150
  61. package/dist/frontend-core/template/src/components/ui/button/button.tsx +0 -68
  62. package/dist/frontend-core/template/src/components/ui/calendar/calendar.stories.tsx +0 -160
  63. package/dist/frontend-core/template/src/components/ui/calendar/calendar.tsx +0 -293
  64. package/dist/frontend-core/template/src/components/ui/card/card.stories.tsx +0 -77
  65. package/dist/frontend-core/template/src/components/ui/card/card.tsx +0 -45
  66. package/dist/frontend-core/template/src/components/ui/card-hover/card-hover.stories.tsx +0 -29
  67. package/dist/frontend-core/template/src/components/ui/card-hover/card-hover.tsx +0 -28
  68. package/dist/frontend-core/template/src/components/ui/carousel/carousel.stories.tsx +0 -154
  69. package/dist/frontend-core/template/src/components/ui/carousel/carousel.tsx +0 -227
  70. package/dist/frontend-core/template/src/components/ui/checkbox/checkbox.stories.tsx +0 -106
  71. package/dist/frontend-core/template/src/components/ui/checkbox/checkbox.tsx +0 -88
  72. package/dist/frontend-core/template/src/components/ui/checkbox/shadcn-checkbox.stories.tsx +0 -90
  73. package/dist/frontend-core/template/src/components/ui/checkbox/shadcn-checkbox.tsx +0 -54
  74. package/dist/frontend-core/template/src/components/ui/collapse/collapse.stories.tsx +0 -52
  75. package/dist/frontend-core/template/src/components/ui/collapse/collapse.tsx +0 -9
  76. package/dist/frontend-core/template/src/components/ui/combobox/combobox.stories.tsx +0 -207
  77. package/dist/frontend-core/template/src/components/ui/combobox/combobox.tsx +0 -79
  78. package/dist/frontend-core/template/src/components/ui/command/command.stories.tsx +0 -186
  79. package/dist/frontend-core/template/src/components/ui/command/command.tsx +0 -165
  80. package/dist/frontend-core/template/src/components/ui/command-palette/command-palette.stories.tsx +0 -160
  81. package/dist/frontend-core/template/src/components/ui/command-palette/command-palette.tsx +0 -134
  82. package/dist/frontend-core/template/src/components/ui/content-frame/content-frame.stories.tsx +0 -198
  83. package/dist/frontend-core/template/src/components/ui/content-frame/content-frame.tsx +0 -100
  84. package/dist/frontend-core/template/src/components/ui/context-menu/context-menu.stories.tsx +0 -78
  85. package/dist/frontend-core/template/src/components/ui/context-menu/context-menu.tsx +0 -179
  86. package/dist/frontend-core/template/src/components/ui/data-grid/cell-variants/cell-variant-types.ts +0 -11
  87. package/dist/frontend-core/template/src/components/ui/data-grid/cell-variants/checkbox-cell.tsx +0 -116
  88. package/dist/frontend-core/template/src/components/ui/data-grid/cell-variants/date-cell.tsx +0 -157
  89. package/dist/frontend-core/template/src/components/ui/data-grid/cell-variants/gantt-cell.tsx +0 -82
  90. package/dist/frontend-core/template/src/components/ui/data-grid/cell-variants/long-text-cell.tsx +0 -180
  91. package/dist/frontend-core/template/src/components/ui/data-grid/cell-variants/multi-select-cell.tsx +0 -280
  92. package/dist/frontend-core/template/src/components/ui/data-grid/cell-variants/number-cell.tsx +0 -169
  93. package/dist/frontend-core/template/src/components/ui/data-grid/cell-variants/react-node-cell.tsx +0 -33
  94. package/dist/frontend-core/template/src/components/ui/data-grid/cell-variants/select-cell.tsx +0 -175
  95. package/dist/frontend-core/template/src/components/ui/data-grid/cell-variants/short-text-cell.tsx +0 -138
  96. package/dist/frontend-core/template/src/components/ui/data-grid/cell-variants/utils/gantt-timeline.tsx +0 -92
  97. package/dist/frontend-core/template/src/components/ui/data-grid/cell-variants/utils/gantt-timerange-picker.tsx +0 -330
  98. package/dist/frontend-core/template/src/components/ui/data-grid/data-grid-cell-wrapper.tsx +0 -212
  99. package/dist/frontend-core/template/src/components/ui/data-grid/data-grid-cell.tsx +0 -157
  100. package/dist/frontend-core/template/src/components/ui/data-grid/data-grid-column-header.tsx +0 -340
  101. package/dist/frontend-core/template/src/components/ui/data-grid/data-grid-context-menu.tsx +0 -271
  102. package/dist/frontend-core/template/src/components/ui/data-grid/data-grid-row.tsx +0 -123
  103. package/dist/frontend-core/template/src/components/ui/data-grid/data-grid-search.tsx +0 -211
  104. package/dist/frontend-core/template/src/components/ui/data-grid/data-grid-types.ts +0 -159
  105. package/dist/frontend-core/template/src/components/ui/data-grid/data-grid-utils.ts +0 -67
  106. package/dist/frontend-core/template/src/components/ui/data-grid/data-grid-view-menu.tsx +0 -360
  107. package/dist/frontend-core/template/src/components/ui/data-grid/data-grid.stories.tsx +0 -780
  108. package/dist/frontend-core/template/src/components/ui/data-grid/data-grid.tsx +0 -217
  109. package/dist/frontend-core/template/src/components/ui/data-grid/hooks/use-callback-ref.ts +0 -22
  110. package/dist/frontend-core/template/src/components/ui/data-grid/hooks/use-data-grid.tsx +0 -1892
  111. package/dist/frontend-core/template/src/components/ui/data-grid/hooks/use-debounced-callback.ts +0 -19
  112. package/dist/frontend-core/template/src/components/ui/data-grid/styles.css +0 -3
  113. package/dist/frontend-core/template/src/components/ui/data-table/context-menu-simple.tsx +0 -141
  114. package/dist/frontend-core/template/src/components/ui/data-table/data-table.stories.tsx +0 -146
  115. package/dist/frontend-core/template/src/components/ui/data-table/data-table.tsx +0 -447
  116. package/dist/frontend-core/template/src/components/ui/data-table/renderers/country-array-cell-renderer.tsx +0 -77
  117. package/dist/frontend-core/template/src/components/ui/data-table/renderers/country-cell-renderer.tsx +0 -56
  118. package/dist/frontend-core/template/src/components/ui/data-table/renderers/favorite-cell-renderer.tsx +0 -68
  119. package/dist/frontend-core/template/src/components/ui/data-table/renderers/links-cell-renderer.tsx +0 -205
  120. package/dist/frontend-core/template/src/components/ui/data-table/utils/columns.ts +0 -351
  121. package/dist/frontend-core/template/src/components/ui/data-table/utils/data-table.utils.ts +0 -49
  122. package/dist/frontend-core/template/src/components/ui/date-picker/date-picker.stories.tsx +0 -149
  123. package/dist/frontend-core/template/src/components/ui/date-picker/date-picker.tsx +0 -30
  124. package/dist/frontend-core/template/src/components/ui/dialog/dialog.stories.tsx +0 -80
  125. package/dist/frontend-core/template/src/components/ui/dialog/dialog.tsx +0 -134
  126. package/dist/frontend-core/template/src/components/ui/drawer/drawer.stories.tsx +0 -104
  127. package/dist/frontend-core/template/src/components/ui/drawer/drawer.tsx +0 -87
  128. package/dist/frontend-core/template/src/components/ui/dropdown-menu/dropdown-menu.stories.tsx +0 -168
  129. package/dist/frontend-core/template/src/components/ui/dropdown-menu/dropdown-menu.tsx +0 -225
  130. package/dist/frontend-core/template/src/components/ui/input/input.stories.tsx +0 -141
  131. package/dist/frontend-core/template/src/components/ui/input/input.tsx +0 -47
  132. package/dist/frontend-core/template/src/components/ui/label/label.stories.tsx +0 -41
  133. package/dist/frontend-core/template/src/components/ui/label/label.tsx +0 -20
  134. package/dist/frontend-core/template/src/components/ui/loader/loader.stories.tsx +0 -45
  135. package/dist/frontend-core/template/src/components/ui/loader/loader.tsx +0 -17
  136. package/dist/frontend-core/template/src/components/ui/mark-value-renderer/mark-value-renderer.stories.tsx +0 -114
  137. package/dist/frontend-core/template/src/components/ui/mark-value-renderer/mark-value-renderer.tsx +0 -48
  138. package/dist/frontend-core/template/src/components/ui/menubar/menu.stories.tsx +0 -134
  139. package/dist/frontend-core/template/src/components/ui/menubar/menubar.tsx +0 -208
  140. package/dist/frontend-core/template/src/components/ui/modal/modal.stories.tsx +0 -297
  141. package/dist/frontend-core/template/src/components/ui/modal/modal.tsx +0 -80
  142. package/dist/frontend-core/template/src/components/ui/navigation-menu/navigation-menu.stories.tsx +0 -213
  143. package/dist/frontend-core/template/src/components/ui/navigation-menu/navigation-menu.tsx +0 -142
  144. package/dist/frontend-core/template/src/components/ui/pagination/pagination.stories.tsx +0 -49
  145. package/dist/frontend-core/template/src/components/ui/pagination/pagination.tsx +0 -84
  146. package/dist/frontend-core/template/src/components/ui/popover/popover.stories.tsx +0 -82
  147. package/dist/frontend-core/template/src/components/ui/popover/popover.tsx +0 -55
  148. package/dist/frontend-core/template/src/components/ui/progress/progress.stories.tsx +0 -80
  149. package/dist/frontend-core/template/src/components/ui/progress/progress.tsx +0 -17
  150. package/dist/frontend-core/template/src/components/ui/radio-group/radio-group.stories.tsx +0 -154
  151. package/dist/frontend-core/template/src/components/ui/radio-group/radio-group.tsx +0 -68
  152. package/dist/frontend-core/template/src/components/ui/resizable/resizable.stories.tsx +0 -73
  153. package/dist/frontend-core/template/src/components/ui/resizable/resizeable.tsx +0 -38
  154. package/dist/frontend-core/template/src/components/ui/scroll-area/scroll-area.stories.tsx +0 -55
  155. package/dist/frontend-core/template/src/components/ui/scroll-area/scroll-area.tsx +0 -39
  156. package/dist/frontend-core/template/src/components/ui/select/select.stories.tsx +0 -297
  157. package/dist/frontend-core/template/src/components/ui/select/select.tsx +0 -227
  158. package/dist/frontend-core/template/src/components/ui/separator/separator.tsx +0 -21
  159. package/dist/frontend-core/template/src/components/ui/separator/seperator.stories.tsx +0 -25
  160. package/dist/frontend-core/template/src/components/ui/sheet/sheet.stories.tsx +0 -45
  161. package/dist/frontend-core/template/src/components/ui/sheet/sheet.tsx +0 -107
  162. package/dist/frontend-core/template/src/components/ui/skeleton/skeleton.stories.tsx +0 -26
  163. package/dist/frontend-core/template/src/components/ui/skeleton/skeleton.tsx +0 -7
  164. package/dist/frontend-core/template/src/components/ui/slider/slider.stories.tsx +0 -101
  165. package/dist/frontend-core/template/src/components/ui/slider/slider.tsx +0 -98
  166. package/dist/frontend-core/template/src/components/ui/spinner/spinner.stories.tsx +0 -19
  167. package/dist/frontend-core/template/src/components/ui/spinner/spinner.tsx +0 -21
  168. package/dist/frontend-core/template/src/components/ui/switch/switch.stories.tsx +0 -33
  169. package/dist/frontend-core/template/src/components/ui/switch/switch.tsx +0 -28
  170. package/dist/frontend-core/template/src/components/ui/tabs/tabs.stories.tsx +0 -215
  171. package/dist/frontend-core/template/src/components/ui/tabs/tabs.tsx +0 -70
  172. package/dist/frontend-core/template/src/components/ui/textarea/textarea.stories.tsx +0 -138
  173. package/dist/frontend-core/template/src/components/ui/textarea/textarea.tsx +0 -40
  174. package/dist/frontend-core/template/src/components/ui/toast/toast.mdx +0 -31
  175. package/dist/frontend-core/template/src/components/ui/toast/toast.stories.tsx +0 -89
  176. package/dist/frontend-core/template/src/components/ui/toggle/toggle.stories.tsx +0 -65
  177. package/dist/frontend-core/template/src/components/ui/toggle/toggle.tsx +0 -38
  178. package/dist/frontend-core/template/src/components/ui/toggle-group/toggle-group.stories.tsx +0 -85
  179. package/dist/frontend-core/template/src/components/ui/toggle-group/toggle-group.tsx +0 -54
  180. package/dist/frontend-core/template/src/components/ui/tooltip/tooltip.stories.tsx +0 -29
  181. package/dist/frontend-core/template/src/components/ui/tooltip/tooltip.tsx +0 -29
@@ -1,340 +0,0 @@
1
- import type { ColumnSort, Header, SortDirection, SortingState, Table } from '@tanstack/react-table'
2
-
3
- import {
4
- BaselineIcon,
5
- CalendarIcon,
6
- CheckSquareIcon,
7
- ChevronDownIcon,
8
- ChevronUpIcon,
9
- EyeOffIcon,
10
- HashIcon,
11
- ListChecksIcon,
12
- ListIcon,
13
- PinIcon,
14
- PinOffIcon,
15
- TextInitialIcon,
16
- XIcon,
17
- } from 'lucide-react'
18
- import * as React from 'react'
19
-
20
- import type { Cell, ColumnMenuRendererFunction } from '@components/ui/data-grid/data-grid-types'
21
- import {
22
- DropdownMenu,
23
- DropdownMenuCheckboxItem,
24
- DropdownMenuContent,
25
- DropdownMenuItem,
26
- DropdownMenuSeparator,
27
- DropdownMenuTrigger,
28
- } from '@components/ui/dropdown-menu/dropdown-menu'
29
- import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from '@components/ui/tooltip/tooltip'
30
- import { cn } from '@lib/utils'
31
-
32
- import { GanttTimeline } from './cell-variants/utils/gantt-timeline'
33
-
34
- function getColumnVariant(variant?: Cell['variant']): {
35
- icon: React.ComponentType<React.SVGProps<SVGSVGElement>>
36
- label: string
37
- } | null {
38
- switch (variant) {
39
- case 'short-text':
40
- return { icon: BaselineIcon, label: 'Short text' }
41
- case 'long-text':
42
- return { icon: TextInitialIcon, label: 'Long text' }
43
- case 'number':
44
- return { icon: HashIcon, label: 'Number' }
45
- case 'select':
46
- return { icon: ListIcon, label: 'Select' }
47
- case 'multi-select':
48
- return { icon: ListChecksIcon, label: 'Multi-select' }
49
- case 'checkbox':
50
- return { icon: CheckSquareIcon, label: 'Checkbox' }
51
- case 'date':
52
- return { icon: CalendarIcon, label: 'Date' }
53
- default:
54
- return null
55
- }
56
- }
57
-
58
- type DataGridColumnHeaderProps<TData, TValue> = {
59
- header: Header<TData, TValue>
60
- table: Table<TData>
61
- } & React.ComponentProps<typeof DropdownMenuTrigger>
62
-
63
- export function DataGridColumnHeader<TData, TValue>({
64
- header,
65
- table,
66
- className,
67
- onPointerDown,
68
- ...props
69
- }: DataGridColumnHeaderProps<TData, TValue>) {
70
- const [open, setOpen] = React.useState(false)
71
-
72
- const column = header.column
73
- const label = column.columnDef.meta?.label
74
- ? column.columnDef.meta.label
75
- : typeof column.columnDef.header === 'string'
76
- ? column.columnDef.header
77
- : column.id
78
-
79
- const isAnyColumnResizing = table.getState().columnSizingInfo.isResizingColumn
80
-
81
- const cellVariant = column.columnDef.meta?.cell
82
- const columnVariant = getColumnVariant(cellVariant?.variant)
83
-
84
- const pinnedPosition = column.getIsPinned()
85
- const isPinnedLeft = pinnedPosition === 'left'
86
- const isPinnedRight = pinnedPosition === 'right'
87
-
88
- const onSortingChange = React.useCallback(
89
- (direction: SortDirection) => {
90
- table.setSorting((prev: SortingState) => {
91
- const existingSortIndex = prev.findIndex((sort) => sort.id === column.id)
92
- const newSort: ColumnSort = {
93
- id: column.id,
94
- desc: direction === 'desc',
95
- }
96
-
97
- if (existingSortIndex >= 0) {
98
- const updated = [...prev]
99
- updated[existingSortIndex] = newSort
100
- return updated
101
- } else {
102
- return [...prev, newSort]
103
- }
104
- })
105
- },
106
- [column.id, table],
107
- )
108
-
109
- const onSortRemove = React.useCallback(() => {
110
- table.setSorting((prev: SortingState) => prev.filter((sort) => sort.id !== column.id))
111
- }, [column.id, table])
112
-
113
- const onLeftPin = React.useCallback(() => {
114
- column.pin('left')
115
- }, [column])
116
-
117
- const onRightPin = React.useCallback(() => {
118
- column.pin('right')
119
- }, [column])
120
-
121
- const onUnpin = React.useCallback(() => {
122
- column.pin(false)
123
- }, [column])
124
-
125
- const onTriggerPointerDown = React.useCallback(
126
- (event: React.PointerEvent<HTMLButtonElement>) => {
127
- onPointerDown?.(event)
128
- if (event.defaultPrevented) {
129
- return
130
- }
131
-
132
- if (event.button !== 0) {
133
- return
134
- }
135
- table.options.meta?.onColumnClick?.(column.id)
136
- },
137
- [table.options.meta, column.id, onPointerDown],
138
- )
139
-
140
- return (
141
- <>
142
- <DropdownMenu open={open} onOpenChange={setOpen}>
143
- <DropdownMenuTrigger
144
- className={cn(
145
- 'flex size-full items-center justify-between gap-2 p-2 text-sm bg-sidebar-accent/80 font-bold hover:bg-secondary/40 data-[state=open]:bg-secondary/40 [&_svg]:size-4',
146
- isAnyColumnResizing && 'pointer-events-none',
147
- className,
148
- )}
149
- onPointerDown={onTriggerPointerDown}
150
- {...props}
151
- >
152
- <div className="flex min-w-0 flex-1 items-center gap-1.5">
153
- {columnVariant && (
154
- <TooltipProvider>
155
- <Tooltip delayDuration={100}>
156
- <TooltipTrigger asChild>
157
- <columnVariant.icon className="size-3.5 shrink-0 text-muted-foreground" />
158
- </TooltipTrigger>
159
- <TooltipContent side="top">
160
- <p>{columnVariant.label}</p>
161
- </TooltipContent>
162
- </Tooltip>
163
- </TooltipProvider>
164
- )}
165
- <span className="truncate">{label}</span>
166
- </div>
167
- <ChevronDownIcon className="shrink-0 text-muted-foreground" />
168
- </DropdownMenuTrigger>
169
- <DropdownMenuContent align="start" sideOffset={0} className="w-60">
170
- {column.getCanSort() && (
171
- <>
172
- <DropdownMenuCheckboxItem
173
- className="relative pr-8 pl-2 [&>span:first-child]:right-2 [&>span:first-child]:left-auto [&_svg]:text-muted-foreground"
174
- checked={column.getIsSorted() === 'asc'}
175
- onClick={() => onSortingChange('asc')}
176
- >
177
- <ChevronUpIcon />
178
- Sort asc
179
- </DropdownMenuCheckboxItem>
180
- <DropdownMenuCheckboxItem
181
- className="relative pr-8 pl-2 [&>span:first-child]:right-2 [&>span:first-child]:left-auto [&_svg]:text-muted-foreground"
182
- checked={column.getIsSorted() === 'desc'}
183
- onClick={() => onSortingChange('desc')}
184
- >
185
- <ChevronDownIcon />
186
- Sort desc
187
- </DropdownMenuCheckboxItem>
188
- {column.getIsSorted() && (
189
- <DropdownMenuItem onClick={onSortRemove}>
190
- <XIcon />
191
- Remove sort
192
- </DropdownMenuItem>
193
- )}
194
- </>
195
- )}
196
- {column.getCanPin() && (
197
- <>
198
- {column.getCanSort() && <DropdownMenuSeparator />}
199
-
200
- {isPinnedLeft ? (
201
- <DropdownMenuItem className="[&_svg]:text-muted-foreground" onClick={onUnpin}>
202
- <PinOffIcon />
203
- Unpin from left
204
- </DropdownMenuItem>
205
- ) : (
206
- <DropdownMenuItem className="[&_svg]:text-muted-foreground" onClick={onLeftPin}>
207
- <PinIcon />
208
- Pin to left
209
- </DropdownMenuItem>
210
- )}
211
- {isPinnedRight ? (
212
- <DropdownMenuItem className="[&_svg]:text-muted-foreground" onClick={onUnpin}>
213
- <PinOffIcon />
214
- Unpin from right
215
- </DropdownMenuItem>
216
- ) : (
217
- <DropdownMenuItem className="[&_svg]:text-muted-foreground" onClick={onRightPin}>
218
- <PinIcon />
219
- Pin to right
220
- </DropdownMenuItem>
221
- )}
222
- </>
223
- )}
224
- {column.getCanHide() && (
225
- <>
226
- <DropdownMenuSeparator />
227
- <DropdownMenuCheckboxItem
228
- className="relative pr-8 pl-2 [&>span:first-child]:right-2 [&>span:first-child]:left-auto [&_svg]:text-muted-foreground"
229
- checked={!column.getIsVisible()}
230
- onClick={() => column.toggleVisibility(false)}
231
- >
232
- <EyeOffIcon />
233
- Hide column
234
- </DropdownMenuCheckboxItem>
235
- </>
236
- )}
237
-
238
- {/* Render header menu footer (custom optional component) */}
239
- {column.columnDef.meta?.headerMenuFooter && (
240
- <>
241
- <DropdownMenuSeparator />
242
- <div className="dropdown-footer">
243
- {isMenuRendererFunction(column.columnDef.meta.headerMenuFooter)
244
- ? column.columnDef.meta.headerMenuFooter({ column, open, onOpenChange: setOpen })
245
- : column.columnDef.meta.headerMenuFooter}
246
- </div>
247
- </>
248
- )}
249
- </DropdownMenuContent>
250
- </DropdownMenu>
251
- {/* Render any variant-provided header component from HeaderComponents. */}
252
- {(() => {
253
- const variant = column.columnDef.meta?.cell?.variant
254
- const variantHeader = variant ? HeaderComponents[variant] : undefined
255
- if (variantHeader) {
256
- return variantHeader({ header, table })
257
- }
258
-
259
- return null
260
- })()}
261
-
262
- {/* Render custom column header component (optional columnDef.meta) */}
263
- {column.columnDef.meta?.headerCustomComponent && (
264
- <>
265
- {isMenuRendererFunction(column.columnDef.meta.headerCustomComponent)
266
- ? column.columnDef.meta.headerCustomComponent({ column })
267
- : column.columnDef.meta.headerCustomComponent}
268
- </>
269
- )}
270
-
271
- {header.column.getCanResize() && <DataGridColumnResizer header={header} table={table} label={label} />}
272
- </>
273
- )
274
- }
275
-
276
- const DataGridColumnResizer = React.memo(DataGridColumnResizerImpl, (prev, next) => {
277
- const prevColumn = prev.header.column
278
- const nextColumn = next.header.column
279
-
280
- if (prevColumn.getIsResizing() !== nextColumn.getIsResizing() || prevColumn.getSize() !== nextColumn.getSize()) {
281
- return false
282
- }
283
-
284
- if (prev.label !== next.label) {
285
- return false
286
- }
287
-
288
- return true
289
- }) as typeof DataGridColumnResizerImpl
290
-
291
- type DataGridColumnResizerProps<TData, TValue> = {
292
- label: string
293
- } & DataGridColumnHeaderProps<TData, TValue>
294
-
295
- function DataGridColumnResizerImpl<TData, TValue>({ header, table, label }: DataGridColumnResizerProps<TData, TValue>) {
296
- const defaultColumnDef = table._getDefaultColumnDef()
297
-
298
- const onDoubleClick = React.useCallback(() => {
299
- header.column.resetSize()
300
- }, [header.column])
301
-
302
- return (
303
- <div
304
- role="separator"
305
- aria-orientation="vertical"
306
- aria-label={`Resize ${label} column`}
307
- aria-valuenow={header.column.getSize()}
308
- aria-valuemin={defaultColumnDef.minSize}
309
- aria-valuemax={defaultColumnDef.maxSize}
310
- tabIndex={0}
311
- className={cn(
312
- 'right-0 absolute top-0 z-50 h-full w-0 touch-none select-none cursor-ew-resize focus:outline-none',
313
- // visible thin line (use right positioning and a stable 1px width)
314
- "before:content-[''] before:absolute before:inset-y-0 before:right-0 before:translate-x-1/2 before:w-px before:h-full before:bg-accent-foreground/70 before:opacity-0 before:transition-opacity before:duration-150",
315
- // large invisible hit area
316
- "after:content-[''] after:absolute after:inset-y-0 after:right-0 after:translate-x-1/2 after:w-[18px] after:h-full after:bg-transparent",
317
- header.column.getIsResizing() ? 'before:opacity-100 before:bg-accent-foreground' : 'hover:before:opacity-100',
318
- )}
319
- style={{ willChange: 'transform, opacity', transform: 'translateZ(0)' }}
320
- onDoubleClick={onDoubleClick}
321
- onMouseDown={header.getResizeHandler()}
322
- onTouchStart={header.getResizeHandler()}
323
- />
324
- )
325
- }
326
-
327
- function isMenuRendererFunction<TData, TValue>(
328
- value: React.ReactNode | ColumnMenuRendererFunction<TData, TValue>,
329
- ): value is ColumnMenuRendererFunction<TData, TValue> {
330
- return typeof value === 'function'
331
- }
332
-
333
- /**
334
- * Optional header components keyed by cell variant. Components receive { header, table } and
335
- * should return a React node (or null). This allows the column header to render variant-specific
336
- * header visuals without coupling the header implementation to variants.
337
- */
338
- export const HeaderComponents: Record<string, (props: any) => React.ReactNode | null> = {
339
- gantt: (props) => <GanttTimeline {...props} />,
340
- }
@@ -1,271 +0,0 @@
1
- import { CopyIcon, EraserIcon, TrashIcon } from '@radix-ui/react-icons'
2
- import type { Table, TableMeta } from '@tanstack/react-table'
3
-
4
- import * as React from 'react'
5
- import { toast } from 'sonner'
6
-
7
- import type { UpdateCell } from '@components/ui/data-grid/data-grid-types'
8
- import { parseCellKey } from '@components/ui/data-grid/data-grid-utils'
9
- import {
10
- DropdownMenu,
11
- DropdownMenuContent,
12
- DropdownMenuItem,
13
- DropdownMenuSeparator,
14
- DropdownMenuTrigger,
15
- } from '@components/ui/dropdown-menu/dropdown-menu'
16
-
17
- type DataGridContextMenuProps<TData> = {
18
- table: Table<TData>
19
- }
20
-
21
- export function DataGridContextMenu<TData>({ table }: DataGridContextMenuProps<TData>) {
22
- const meta = table.options.meta
23
- const contextMenu = meta?.contextMenu
24
- const onContextMenuOpenChange = meta?.onContextMenuOpenChange
25
- const selectionState = meta?.selectionState
26
- const dataGridRef = meta?.dataGridRef
27
- const onDataUpdate = meta?.onDataUpdate
28
- const onRowsDelete = meta?.onRowsDelete
29
-
30
- if (!contextMenu) {
31
- return null
32
- }
33
-
34
- return (
35
- <ContextMenu
36
- table={table}
37
- dataGridRef={dataGridRef}
38
- contextMenu={contextMenu}
39
- onContextMenuOpenChange={onContextMenuOpenChange}
40
- selectionState={selectionState}
41
- onDataUpdate={onDataUpdate}
42
- onRowsDelete={onRowsDelete}
43
- />
44
- )
45
- }
46
-
47
- type ContextMenuProps<TData> = {
48
- table: Table<TData>
49
- } & Pick<
50
- TableMeta<TData>,
51
- 'dataGridRef' | 'onContextMenuOpenChange' | 'selectionState' | 'onDataUpdate' | 'onRowsDelete'
52
- > &
53
- Required<Pick<TableMeta<TData>, 'contextMenu'>>
54
-
55
- const ContextMenu = React.memo(ContextMenuImpl, (prev, next) => {
56
- if (prev.contextMenu.open !== next.contextMenu.open) {
57
- return false
58
- }
59
- if (!next.contextMenu.open) {
60
- return true
61
- }
62
- if (prev.contextMenu.x !== next.contextMenu.x) {
63
- return false
64
- }
65
- if (prev.contextMenu.y !== next.contextMenu.y) {
66
- return false
67
- }
68
-
69
- const prevSize = prev.selectionState?.selectedCells?.size ?? 0
70
- const nextSize = next.selectionState?.selectedCells?.size ?? 0
71
- if (prevSize !== nextSize) {
72
- return false
73
- }
74
-
75
- return true
76
- }) as typeof ContextMenuImpl
77
-
78
- function ContextMenuImpl<TData>({
79
- table,
80
- dataGridRef,
81
- contextMenu,
82
- onContextMenuOpenChange,
83
- selectionState,
84
- onDataUpdate,
85
- onRowsDelete,
86
- }: ContextMenuProps<TData>) {
87
- const triggerStyle = React.useMemo<React.CSSProperties>(
88
- () => ({
89
- position: 'fixed',
90
- left: `${contextMenu.x}px`,
91
- top: `${contextMenu.y}px`,
92
- width: '1px',
93
- height: '1px',
94
- padding: 0,
95
- margin: 0,
96
- border: 'none',
97
- background: 'transparent',
98
- pointerEvents: 'none',
99
- opacity: 0,
100
- }),
101
- [contextMenu.x, contextMenu.y],
102
- )
103
-
104
- const onCloseAutoFocus: NonNullable<React.ComponentProps<typeof DropdownMenuContent>['onCloseAutoFocus']> =
105
- React.useCallback(
106
- (event) => {
107
- event.preventDefault()
108
- dataGridRef?.current?.focus()
109
- },
110
- [dataGridRef],
111
- )
112
-
113
- const onCopy = React.useCallback(async () => {
114
- if (!selectionState?.selectedCells || selectionState.selectedCells.size === 0) {
115
- return
116
- }
117
-
118
- const rows = table.getRowModel().rows
119
- const columnIds: string[] = []
120
-
121
- const selectedCellsArray = Array.from(selectionState.selectedCells)
122
- for (const cellKey of selectedCellsArray) {
123
- const { columnId } = parseCellKey(cellKey)
124
- if (columnId && !columnIds.includes(columnId)) {
125
- columnIds.push(columnId)
126
- }
127
- }
128
-
129
- const cellData = new Map<string, string>()
130
- for (const cellKey of selectedCellsArray) {
131
- const { rowIndex, columnId } = parseCellKey(cellKey)
132
- const row = rows[rowIndex]
133
- if (row) {
134
- const cell = row.getVisibleCells().find((c) => c.column.id === columnId)
135
- if (cell) {
136
- const value = cell.getValue()
137
- cellData.set(cellKey, String(value ?? ''))
138
- }
139
- }
140
- }
141
-
142
- const rowIndices = new Set<number>()
143
- const colIndices = new Set<number>()
144
-
145
- for (const cellKey of selectedCellsArray) {
146
- const { rowIndex, columnId } = parseCellKey(cellKey)
147
- rowIndices.add(rowIndex)
148
- const colIndex = columnIds.indexOf(columnId)
149
- if (colIndex >= 0) {
150
- colIndices.add(colIndex)
151
- }
152
- }
153
-
154
- const sortedRowIndices = Array.from(rowIndices).sort((a, b) => a - b)
155
- const sortedColIndices = Array.from(colIndices).sort((a, b) => a - b)
156
- const sortedColumnIds = sortedColIndices.map((i) => columnIds[i])
157
-
158
- const tsvData = sortedRowIndices
159
- .map((rowIndex) =>
160
- sortedColumnIds
161
- .map((columnId) => {
162
- const cellKey = `${rowIndex}:${columnId}`
163
- return cellData.get(cellKey) ?? ''
164
- })
165
- .join('\t'),
166
- )
167
- .join('\n')
168
-
169
- await navigator.clipboard.writeText(tsvData)
170
- toast.success(
171
- `${selectionState.selectedCells.size} cell${selectionState.selectedCells.size !== 1 ? 's' : ''} copied`,
172
- )
173
- }, [table, selectionState])
174
-
175
- // Determine whether the selected cells are all editable. If any selected cell belongs to a non-editable column (meta.editable === false), disable the Clear action.
176
- const canClear = React.useMemo(() => {
177
- if (!selectionState?.selectedCells || selectionState.selectedCells.size === 0) {
178
- return false
179
- }
180
-
181
- const visibleCols = table.getVisibleLeafColumns()
182
- const rows = table.getRowModel().rows
183
-
184
- for (const cellKey of selectionState.selectedCells) {
185
- const { rowIndex, columnId } = parseCellKey(cellKey)
186
- if (!columnId) {
187
- continue
188
- }
189
- const col = visibleCols.find((c) => c.id === columnId)
190
- const editable = col?.columnDef?.meta?.editable
191
-
192
- if (editable === false) {
193
- return false
194
- }
195
-
196
- if (typeof editable === 'function') {
197
- const row = rows[rowIndex]
198
- if (row && !editable(row.original)) {
199
- return false
200
- }
201
- }
202
- }
203
-
204
- return true
205
- }, [selectionState, table])
206
-
207
- const onClear = React.useCallback(() => {
208
- if (!selectionState?.selectedCells || selectionState.selectedCells.size === 0) {
209
- return
210
- }
211
-
212
- if (!canClear) {
213
- return
214
- }
215
-
216
- const updates: UpdateCell[] = []
217
-
218
- for (const cellKey of selectionState.selectedCells) {
219
- const { rowIndex, columnId } = parseCellKey(cellKey)
220
- updates.push({ rowIndex, columnId, value: '' })
221
- }
222
-
223
- onDataUpdate?.(updates)
224
-
225
- toast.success(`${updates.length} cell${updates.length !== 1 ? 's' : ''} cleared`)
226
- }, [onDataUpdate, selectionState, canClear])
227
-
228
- const onDelete = React.useCallback(async () => {
229
- if (!selectionState?.selectedCells || selectionState.selectedCells.size === 0) {
230
- return
231
- }
232
-
233
- const rowIndices = new Set<number>()
234
- for (const cellKey of selectionState.selectedCells) {
235
- const { rowIndex } = parseCellKey(cellKey)
236
- rowIndices.add(rowIndex)
237
- }
238
-
239
- const rowIndicesArray = Array.from(rowIndices).sort((a, b) => a - b)
240
- const rowCount = rowIndicesArray.length
241
-
242
- await onRowsDelete?.(rowIndicesArray)
243
-
244
- toast.success(`${rowCount} row${rowCount !== 1 ? 's' : ''} deleted`)
245
- }, [onRowsDelete, selectionState])
246
-
247
- return (
248
- <DropdownMenu open={contextMenu.open} onOpenChange={onContextMenuOpenChange}>
249
- <DropdownMenuTrigger style={triggerStyle} />
250
- <DropdownMenuContent data-grid-popover="" align="start" className="w-48" onCloseAutoFocus={onCloseAutoFocus}>
251
- <DropdownMenuItem onSelect={onCopy}>
252
- <CopyIcon />
253
- Copy
254
- </DropdownMenuItem>
255
- <DropdownMenuItem onSelect={onClear} disabled={!canClear}>
256
- <EraserIcon />
257
- Clear
258
- </DropdownMenuItem>
259
- {onRowsDelete && (
260
- <>
261
- <DropdownMenuSeparator />
262
- <DropdownMenuItem variant="destructive" onSelect={onDelete}>
263
- <TrashIcon />
264
- Delete rows
265
- </DropdownMenuItem>
266
- </>
267
- )}
268
- </DropdownMenuContent>
269
- </DropdownMenu>
270
- )
271
- }