@postxl/generators 1.1.0 → 1.2.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 (169) hide show
  1. package/dist/backend-core/template/apps/api/src/e2e.ts +13 -4
  2. package/dist/e2e/e2e.generator.js +2 -14
  3. package/dist/e2e/e2e.generator.js.map +1 -1
  4. package/dist/e2e/generators/docker-sh.generator.d.ts +2 -0
  5. package/dist/e2e/generators/docker-sh.generator.js +25 -0
  6. package/dist/e2e/generators/docker-sh.generator.js.map +1 -0
  7. package/dist/frontend-core/frontend.generator.d.ts +0 -58
  8. package/dist/frontend-core/frontend.generator.js +6 -172
  9. package/dist/frontend-core/frontend.generator.js.map +1 -1
  10. package/dist/frontend-core/template/README.md +1 -1
  11. package/dist/frontend-core/template/src/components/admin/table-filter.tsx +1 -5
  12. package/dist/frontend-core/template/src/components/ui/color-mode-toggle/color-mode-toggle.tsx +10 -4
  13. package/dist/frontend-core/template/src/context-providers/auth-context-provider.tsx +2 -5
  14. package/dist/frontend-core/template/src/pages/dashboard/dashboard.page.tsx +2 -3
  15. package/dist/frontend-core/template/src/pages/error/default-error.page.tsx +1 -1
  16. package/dist/frontend-core/template/src/pages/error/not-found-error.page.tsx +1 -1
  17. package/dist/frontend-core/template/src/styles/styles.css +13 -1
  18. package/dist/frontend-core/template/tsconfig.json +2 -0
  19. package/dist/frontend-core/types/component.d.ts +1 -1
  20. package/dist/frontend-forms/generators/discriminated-union/fields.generator.js +4 -6
  21. package/dist/frontend-forms/generators/discriminated-union/fields.generator.js.map +1 -1
  22. package/dist/frontend-forms/generators/discriminated-union/inputs.generator.js +1 -1
  23. package/dist/frontend-forms/generators/discriminated-union/inputs.generator.js.map +1 -1
  24. package/dist/frontend-forms/generators/enum/inputs.generator.js +1 -1
  25. package/dist/frontend-forms/generators/enum/inputs.generator.js.map +1 -1
  26. package/dist/frontend-forms/generators/model/forms.generator.js +8 -12
  27. package/dist/frontend-forms/generators/model/forms.generator.js.map +1 -1
  28. package/dist/frontend-forms/generators/model/inputs.generator.js +2 -6
  29. package/dist/frontend-forms/generators/model/inputs.generator.js.map +1 -1
  30. package/dist/frontend-forms/template/src/components/ui/field/field.tsx +1 -4
  31. package/dist/frontend-tables/generators/model-table.generator.js +1 -5
  32. package/dist/frontend-tables/generators/model-table.generator.js.map +1 -1
  33. package/package.json +3 -2
  34. package/dist/e2e/template/scripts/docker.sh +0 -17
  35. package/dist/frontend-core/template/src/components/ui/accordion/accordion.stories.tsx +0 -47
  36. package/dist/frontend-core/template/src/components/ui/accordion/accordion.tsx +0 -52
  37. package/dist/frontend-core/template/src/components/ui/admin-sidebar/admin-sidebar.tsx +0 -195
  38. package/dist/frontend-core/template/src/components/ui/alert/alert.stories.tsx +0 -61
  39. package/dist/frontend-core/template/src/components/ui/alert/alert.tsx +0 -45
  40. package/dist/frontend-core/template/src/components/ui/alert-dialog/alert-dialog.stories.tsx +0 -52
  41. package/dist/frontend-core/template/src/components/ui/alert-dialog/alert-dialog.tsx +0 -105
  42. package/dist/frontend-core/template/src/components/ui/avatar/avatar.stories.tsx +0 -30
  43. package/dist/frontend-core/template/src/components/ui/avatar/avatar.tsx +0 -39
  44. package/dist/frontend-core/template/src/components/ui/badge/badge.stories.tsx +0 -78
  45. package/dist/frontend-core/template/src/components/ui/badge/badge.tsx +0 -48
  46. package/dist/frontend-core/template/src/components/ui/breadcrumb/breadcrumb.stories.tsx +0 -67
  47. package/dist/frontend-core/template/src/components/ui/breadcrumb/breadcrumb.tsx +0 -85
  48. package/dist/frontend-core/template/src/components/ui/button/button.stories.tsx +0 -150
  49. package/dist/frontend-core/template/src/components/ui/button/button.tsx +0 -68
  50. package/dist/frontend-core/template/src/components/ui/calendar/calendar.stories.tsx +0 -160
  51. package/dist/frontend-core/template/src/components/ui/calendar/calendar.tsx +0 -293
  52. package/dist/frontend-core/template/src/components/ui/card/card.stories.tsx +0 -77
  53. package/dist/frontend-core/template/src/components/ui/card/card.tsx +0 -45
  54. package/dist/frontend-core/template/src/components/ui/card-hover/card-hover.stories.tsx +0 -29
  55. package/dist/frontend-core/template/src/components/ui/card-hover/card-hover.tsx +0 -28
  56. package/dist/frontend-core/template/src/components/ui/carousel/carousel.stories.tsx +0 -154
  57. package/dist/frontend-core/template/src/components/ui/carousel/carousel.tsx +0 -227
  58. package/dist/frontend-core/template/src/components/ui/checkbox/checkbox.stories.tsx +0 -106
  59. package/dist/frontend-core/template/src/components/ui/checkbox/checkbox.tsx +0 -88
  60. package/dist/frontend-core/template/src/components/ui/checkbox/shadcn-checkbox.stories.tsx +0 -90
  61. package/dist/frontend-core/template/src/components/ui/checkbox/shadcn-checkbox.tsx +0 -54
  62. package/dist/frontend-core/template/src/components/ui/collapse/collapse.stories.tsx +0 -52
  63. package/dist/frontend-core/template/src/components/ui/collapse/collapse.tsx +0 -9
  64. package/dist/frontend-core/template/src/components/ui/combobox/combobox.stories.tsx +0 -207
  65. package/dist/frontend-core/template/src/components/ui/combobox/combobox.tsx +0 -79
  66. package/dist/frontend-core/template/src/components/ui/command/command.stories.tsx +0 -186
  67. package/dist/frontend-core/template/src/components/ui/command/command.tsx +0 -165
  68. package/dist/frontend-core/template/src/components/ui/command-palette/command-palette.stories.tsx +0 -160
  69. package/dist/frontend-core/template/src/components/ui/command-palette/command-palette.tsx +0 -134
  70. package/dist/frontend-core/template/src/components/ui/content-frame/content-frame.stories.tsx +0 -198
  71. package/dist/frontend-core/template/src/components/ui/content-frame/content-frame.tsx +0 -100
  72. package/dist/frontend-core/template/src/components/ui/context-menu/context-menu.stories.tsx +0 -78
  73. package/dist/frontend-core/template/src/components/ui/context-menu/context-menu.tsx +0 -179
  74. package/dist/frontend-core/template/src/components/ui/data-grid/cell-variants/cell-variant-types.ts +0 -11
  75. package/dist/frontend-core/template/src/components/ui/data-grid/cell-variants/checkbox-cell.tsx +0 -116
  76. package/dist/frontend-core/template/src/components/ui/data-grid/cell-variants/date-cell.tsx +0 -157
  77. package/dist/frontend-core/template/src/components/ui/data-grid/cell-variants/gantt-cell.tsx +0 -82
  78. package/dist/frontend-core/template/src/components/ui/data-grid/cell-variants/long-text-cell.tsx +0 -180
  79. package/dist/frontend-core/template/src/components/ui/data-grid/cell-variants/multi-select-cell.tsx +0 -280
  80. package/dist/frontend-core/template/src/components/ui/data-grid/cell-variants/number-cell.tsx +0 -169
  81. package/dist/frontend-core/template/src/components/ui/data-grid/cell-variants/react-node-cell.tsx +0 -33
  82. package/dist/frontend-core/template/src/components/ui/data-grid/cell-variants/select-cell.tsx +0 -175
  83. package/dist/frontend-core/template/src/components/ui/data-grid/cell-variants/short-text-cell.tsx +0 -138
  84. package/dist/frontend-core/template/src/components/ui/data-grid/cell-variants/utils/gantt-timeline.tsx +0 -92
  85. package/dist/frontend-core/template/src/components/ui/data-grid/cell-variants/utils/gantt-timerange-picker.tsx +0 -330
  86. package/dist/frontend-core/template/src/components/ui/data-grid/data-grid-cell-wrapper.tsx +0 -212
  87. package/dist/frontend-core/template/src/components/ui/data-grid/data-grid-cell.tsx +0 -157
  88. package/dist/frontend-core/template/src/components/ui/data-grid/data-grid-column-header.tsx +0 -340
  89. package/dist/frontend-core/template/src/components/ui/data-grid/data-grid-context-menu.tsx +0 -271
  90. package/dist/frontend-core/template/src/components/ui/data-grid/data-grid-row.tsx +0 -123
  91. package/dist/frontend-core/template/src/components/ui/data-grid/data-grid-search.tsx +0 -211
  92. package/dist/frontend-core/template/src/components/ui/data-grid/data-grid-types.ts +0 -159
  93. package/dist/frontend-core/template/src/components/ui/data-grid/data-grid-utils.ts +0 -67
  94. package/dist/frontend-core/template/src/components/ui/data-grid/data-grid-view-menu.tsx +0 -360
  95. package/dist/frontend-core/template/src/components/ui/data-grid/data-grid.stories.tsx +0 -780
  96. package/dist/frontend-core/template/src/components/ui/data-grid/data-grid.tsx +0 -217
  97. package/dist/frontend-core/template/src/components/ui/data-grid/hooks/use-callback-ref.ts +0 -22
  98. package/dist/frontend-core/template/src/components/ui/data-grid/hooks/use-data-grid.tsx +0 -1892
  99. package/dist/frontend-core/template/src/components/ui/data-grid/hooks/use-debounced-callback.ts +0 -19
  100. package/dist/frontend-core/template/src/components/ui/data-grid/styles.css +0 -3
  101. package/dist/frontend-core/template/src/components/ui/data-table/context-menu-simple.tsx +0 -141
  102. package/dist/frontend-core/template/src/components/ui/data-table/data-table.stories.tsx +0 -146
  103. package/dist/frontend-core/template/src/components/ui/data-table/data-table.tsx +0 -447
  104. package/dist/frontend-core/template/src/components/ui/data-table/renderers/country-array-cell-renderer.tsx +0 -77
  105. package/dist/frontend-core/template/src/components/ui/data-table/renderers/country-cell-renderer.tsx +0 -56
  106. package/dist/frontend-core/template/src/components/ui/data-table/renderers/favorite-cell-renderer.tsx +0 -68
  107. package/dist/frontend-core/template/src/components/ui/data-table/renderers/links-cell-renderer.tsx +0 -205
  108. package/dist/frontend-core/template/src/components/ui/data-table/utils/columns.ts +0 -351
  109. package/dist/frontend-core/template/src/components/ui/data-table/utils/data-table.utils.ts +0 -49
  110. package/dist/frontend-core/template/src/components/ui/date-picker/date-picker.stories.tsx +0 -149
  111. package/dist/frontend-core/template/src/components/ui/date-picker/date-picker.tsx +0 -30
  112. package/dist/frontend-core/template/src/components/ui/dialog/dialog.stories.tsx +0 -80
  113. package/dist/frontend-core/template/src/components/ui/dialog/dialog.tsx +0 -134
  114. package/dist/frontend-core/template/src/components/ui/drawer/drawer.stories.tsx +0 -104
  115. package/dist/frontend-core/template/src/components/ui/drawer/drawer.tsx +0 -87
  116. package/dist/frontend-core/template/src/components/ui/dropdown-menu/dropdown-menu.stories.tsx +0 -168
  117. package/dist/frontend-core/template/src/components/ui/dropdown-menu/dropdown-menu.tsx +0 -225
  118. package/dist/frontend-core/template/src/components/ui/input/input.stories.tsx +0 -141
  119. package/dist/frontend-core/template/src/components/ui/input/input.tsx +0 -47
  120. package/dist/frontend-core/template/src/components/ui/label/label.stories.tsx +0 -41
  121. package/dist/frontend-core/template/src/components/ui/label/label.tsx +0 -20
  122. package/dist/frontend-core/template/src/components/ui/loader/loader.stories.tsx +0 -45
  123. package/dist/frontend-core/template/src/components/ui/loader/loader.tsx +0 -17
  124. package/dist/frontend-core/template/src/components/ui/mark-value-renderer/mark-value-renderer.stories.tsx +0 -114
  125. package/dist/frontend-core/template/src/components/ui/mark-value-renderer/mark-value-renderer.tsx +0 -48
  126. package/dist/frontend-core/template/src/components/ui/menubar/menu.stories.tsx +0 -134
  127. package/dist/frontend-core/template/src/components/ui/menubar/menubar.tsx +0 -208
  128. package/dist/frontend-core/template/src/components/ui/modal/modal.stories.tsx +0 -297
  129. package/dist/frontend-core/template/src/components/ui/modal/modal.tsx +0 -80
  130. package/dist/frontend-core/template/src/components/ui/navigation-menu/navigation-menu.stories.tsx +0 -213
  131. package/dist/frontend-core/template/src/components/ui/navigation-menu/navigation-menu.tsx +0 -142
  132. package/dist/frontend-core/template/src/components/ui/pagination/pagination.stories.tsx +0 -49
  133. package/dist/frontend-core/template/src/components/ui/pagination/pagination.tsx +0 -84
  134. package/dist/frontend-core/template/src/components/ui/popover/popover.stories.tsx +0 -82
  135. package/dist/frontend-core/template/src/components/ui/popover/popover.tsx +0 -55
  136. package/dist/frontend-core/template/src/components/ui/progress/progress.stories.tsx +0 -80
  137. package/dist/frontend-core/template/src/components/ui/progress/progress.tsx +0 -17
  138. package/dist/frontend-core/template/src/components/ui/radio-group/radio-group.stories.tsx +0 -154
  139. package/dist/frontend-core/template/src/components/ui/radio-group/radio-group.tsx +0 -68
  140. package/dist/frontend-core/template/src/components/ui/resizable/resizable.stories.tsx +0 -73
  141. package/dist/frontend-core/template/src/components/ui/resizable/resizeable.tsx +0 -38
  142. package/dist/frontend-core/template/src/components/ui/scroll-area/scroll-area.stories.tsx +0 -55
  143. package/dist/frontend-core/template/src/components/ui/scroll-area/scroll-area.tsx +0 -39
  144. package/dist/frontend-core/template/src/components/ui/select/select.stories.tsx +0 -297
  145. package/dist/frontend-core/template/src/components/ui/select/select.tsx +0 -227
  146. package/dist/frontend-core/template/src/components/ui/separator/separator.tsx +0 -21
  147. package/dist/frontend-core/template/src/components/ui/separator/seperator.stories.tsx +0 -25
  148. package/dist/frontend-core/template/src/components/ui/sheet/sheet.stories.tsx +0 -45
  149. package/dist/frontend-core/template/src/components/ui/sheet/sheet.tsx +0 -107
  150. package/dist/frontend-core/template/src/components/ui/skeleton/skeleton.stories.tsx +0 -26
  151. package/dist/frontend-core/template/src/components/ui/skeleton/skeleton.tsx +0 -7
  152. package/dist/frontend-core/template/src/components/ui/slider/slider.stories.tsx +0 -101
  153. package/dist/frontend-core/template/src/components/ui/slider/slider.tsx +0 -98
  154. package/dist/frontend-core/template/src/components/ui/spinner/spinner.stories.tsx +0 -19
  155. package/dist/frontend-core/template/src/components/ui/spinner/spinner.tsx +0 -21
  156. package/dist/frontend-core/template/src/components/ui/switch/switch.stories.tsx +0 -33
  157. package/dist/frontend-core/template/src/components/ui/switch/switch.tsx +0 -28
  158. package/dist/frontend-core/template/src/components/ui/tabs/tabs.stories.tsx +0 -215
  159. package/dist/frontend-core/template/src/components/ui/tabs/tabs.tsx +0 -70
  160. package/dist/frontend-core/template/src/components/ui/textarea/textarea.stories.tsx +0 -138
  161. package/dist/frontend-core/template/src/components/ui/textarea/textarea.tsx +0 -40
  162. package/dist/frontend-core/template/src/components/ui/toast/toast.mdx +0 -31
  163. package/dist/frontend-core/template/src/components/ui/toast/toast.stories.tsx +0 -89
  164. package/dist/frontend-core/template/src/components/ui/toggle/toggle.stories.tsx +0 -65
  165. package/dist/frontend-core/template/src/components/ui/toggle/toggle.tsx +0 -38
  166. package/dist/frontend-core/template/src/components/ui/toggle-group/toggle-group.stories.tsx +0 -85
  167. package/dist/frontend-core/template/src/components/ui/toggle-group/toggle-group.tsx +0 -54
  168. package/dist/frontend-core/template/src/components/ui/tooltip/tooltip.stories.tsx +0 -29
  169. package/dist/frontend-core/template/src/components/ui/tooltip/tooltip.tsx +0 -29
@@ -1,447 +0,0 @@
1
- import '@glideapps/glide-data-grid/dist/index.css'
2
-
3
- import { type Theme as ContextTheme, useTheme } from '@context-providers/theme-context-provider'
4
- import {
5
- type CellClickedEventArgs,
6
- DataEditor,
7
- type DataEditorProps,
8
- type DataEditorRef,
9
- type GridCell,
10
- GridCellKind,
11
- type GridColumn,
12
- type GridMouseEventArgs,
13
- type Item,
14
- type LoadingCell,
15
- type Rectangle,
16
- Theme,
17
- } from '@glideapps/glide-data-grid'
18
- import { ParentSize } from '@visx/responsive'
19
-
20
- import { type Dispatch, ReactNode, type SetStateAction, useCallback, useEffect, useRef, useState } from 'react'
21
-
22
- import { Loader } from '@components/ui/loader/loader'
23
-
24
- import { countryArrayDataTableRenderer } from './renderers/country-array-cell-renderer'
25
- import { countryDataTableRenderer } from './renderers/country-cell-renderer'
26
- import { favoriteDataTableRenderer } from './renderers/favorite-cell-renderer'
27
- import { linksTableRenderer } from './renderers/links-cell-renderer'
28
- import { getTableTheme, isHoverableRenderer } from './utils/data-table.utils'
29
- import type { ContextMenuAction, ContextMenuConfig } from './context-menu-simple'
30
-
31
- export type TableSort<FieldName extends string> = {
32
- field: FieldName
33
- order: 'asc' | 'desc' | null
34
- }
35
-
36
- export type DataTableProps<T extends object, ColumnId extends string> = {
37
- data: T[]
38
- columns: DataTableColumn<T, ColumnId>[]
39
- totalRows?: number
40
- onEndReached?: () => void
41
- sortHandler?: (columnId: ColumnId, sort: 'asc' | 'desc' | null) => void
42
- freezeColumns?: number
43
- currentSort?: TableSort<ColumnId>
44
- isLoading?: boolean
45
- isLoadingLabel?: string
46
- hasHeaderGroup?: boolean
47
- footer?: ReactNode
48
- /**
49
- * E2E test_id to identify the button.
50
- */
51
- __e2e_test_id__?: string
52
-
53
- /**
54
- * Callback function to be called when the user right-clicks on a cell.
55
- * This function should be used to show and position the context menu.
56
- */
57
- setContextMenu?: Dispatch<SetStateAction<ContextMenuConfig>>
58
- } & Omit<DataEditorProps, 'columns' | 'rows' | 'getCellContent'>
59
-
60
- /**
61
- * We take the `GridColumn` type from the `@glideapps/glide-data-grid` so the
62
- * user can use all of its props and extend it to include the `toCell`,
63
- * `onFilter`, and `onSort` functions.
64
- */
65
- export type DataTableColumn<Row extends object, ColumnId extends string> = GridColumn & {
66
- id: ColumnId
67
-
68
- tooltip?: string
69
-
70
- /**
71
- * We use this to render the cell content to the column. This then uses the
72
- * `GridCell` type to render the content/data.
73
- */
74
- toCell: (item: Row) => GridCell
75
-
76
- /**
77
- * The function that performs an action when the user clicks on a cell in this column.
78
- */
79
- onClick?: (row: Row, event: CellClickedEventArgs) => void
80
-
81
- /**
82
- * The function that returns context actions for a given row. Will be called when a cell in this column is right-clicked.
83
- */
84
- onContextMenu?: OnContextMenuCallback<Row>
85
-
86
- sortComparator: (row1: Row, row2: Row, direction: 'asc' | 'desc' | null) => number
87
- }
88
-
89
- export type OnContextMenuCallback<Row> = (row: Row, event: CellClickedEventArgs) => ContextMenuAction[]
90
-
91
- /**
92
- * We take the `GridColumn` type from the `@glideapps/glide-data-grid` so the
93
- * user can use all of its props and extend it to include the `toCell`,
94
- * `onFilter`, and `onSort` functions.
95
- */
96
- export type DataGridColumn<Row extends object, ColumnId extends string> = GridColumn & {
97
- id: ColumnId
98
- /**
99
- * We use this to render the cell content to the column. This then uses the
100
- * `GridCell` type to render the content/data.
101
- */
102
- toCell: (item: Row) => GridCell
103
-
104
- onClick?: (rowData: Row, event: CellClickedEventArgs) => void
105
- }
106
-
107
- type ManagedDataTableColumn<Row extends object, ColumnId extends string> = DataTableColumn<Row, ColumnId> & {
108
- activeSort: 'asc' | 'desc' | null
109
- }
110
-
111
- export function DataTable<Row extends object, ColumnId extends string = string>({
112
- data,
113
- columns,
114
- onEndReached,
115
- sortHandler,
116
- freezeColumns,
117
- currentSort,
118
- __e2e_test_id__,
119
- isLoading,
120
- isLoadingLabel = 'Loading...',
121
- hasHeaderGroup,
122
- footer,
123
- setContextMenu,
124
- ...delegated
125
- }: DataTableProps<Row, ColumnId>) {
126
- const sortOrder = currentSort?.order === 'asc' ? 'sortAscending' : 'sortDescending'
127
- const [managedColumns, setManagedColumns] = useState<ManagedDataTableColumn<Row, ColumnId>[]>([])
128
- const { brand, colorMode } = useTheme()
129
- const theme: ContextTheme = `${brand} ${colorMode}`
130
- const [tableTheme, setTableTheme] = useState<Partial<Theme> | undefined>()
131
-
132
- useEffect(() => {
133
- const observer = new MutationObserver(() => {
134
- setTableTheme(getTableTheme(theme))
135
- })
136
-
137
- observer.observe(document.documentElement, { attributes: true, attributeFilter: ['style', 'class'] })
138
-
139
- return () => observer.disconnect()
140
- }, [theme])
141
-
142
- useEffect(() => {
143
- setManagedColumns(
144
- columns.map((col) => ({
145
- ...col,
146
- activeSort: currentSort?.field === col.id ? currentSort.order : null,
147
- isDraggable: true,
148
- icon: currentSort?.field === col.id ? sortOrder : undefined,
149
- })),
150
- )
151
- }, [columns, currentSort, sortOrder])
152
-
153
- const [visibleRegion, setVisibleRegion] = useState<Rectangle>({
154
- x: 0,
155
- y: 0,
156
- width: 0,
157
- height: 0,
158
- })
159
-
160
- // const dataEditorTheme: Partial<Theme> = useMemo(() => getTableTheme(), [theme === 'dark'])
161
-
162
- useEffect(() => {
163
- if (data.length > 1 && visibleRegion.y + visibleRegion.height > data.length - 20) {
164
- onEndReached?.()
165
- }
166
- }, [visibleRegion, onEndReached, data.length])
167
-
168
- const getRowContent = useCallback(
169
- (row: number) => {
170
- return data[row]
171
- },
172
- [data],
173
- )
174
-
175
- const getCellContent = useCallback(
176
- ([col, row]: Item) => {
177
- const rowData = getRowContent(row)
178
-
179
- if (!rowData) {
180
- return { kind: GridCellKind.Loading, allowOverlay: true } satisfies LoadingCell
181
- }
182
-
183
- const toCell = managedColumns[col].toCell
184
-
185
- return toCell(rowData)
186
- },
187
- [getRowContent, managedColumns],
188
- )
189
-
190
- const dataEditorRef = useRef<DataEditorRef>(null)
191
- const tableWrapperRef = useRef<HTMLDivElement>(null)
192
-
193
- const onRightClick = useCallback(
194
- ([col, row]: Item, event: CellClickedEventArgs) => {
195
- if (!dataEditorRef?.current) {
196
- return
197
- }
198
- const cellBounds: Rectangle | undefined = dataEditorRef.current.getBounds(col, row)
199
- if (!cellBounds) {
200
- return
201
- }
202
- const position = {
203
- x: cellBounds.x + event.localEventX,
204
- y: cellBounds.y + event.localEventY,
205
- }
206
-
207
- // Uncomment to get coordinates during test creation
208
- // console.log('absolute', position)
209
- // console.log('relative', { x: event.localEventX, y: event.localEventY })
210
-
211
- if (!setContextMenu) {
212
- return
213
- }
214
- const rowData = getRowContent(row)
215
- if (!rowData) {
216
- return { kind: GridCellKind.Loading, allowOverlay: true } satisfies LoadingCell
217
- }
218
-
219
- const onContextMenuFn = managedColumns[col].onContextMenu
220
- if (!onContextMenuFn) {
221
- return
222
- }
223
-
224
- const actions = onContextMenuFn(rowData, event)
225
- if (!actions) {
226
- return
227
- }
228
- event.preventDefault()
229
-
230
- setContextMenu({
231
- position,
232
- actions,
233
- })
234
- },
235
- [dataEditorRef, setContextMenu, getRowContent, managedColumns],
236
- )
237
-
238
- const [hoveredCellData, setHoveredCellData] = useState<{
239
- x: number
240
- y: number
241
- data: any
242
- hoverOverlay?: (data: any) => React.ReactNode
243
- } | null>(null)
244
-
245
- const [hoveredHeader, setHoveredHeader] = useState<{
246
- columnId: number
247
- x: number
248
- y: number
249
- } | null>(null)
250
- const onItemHovered = useCallback(
251
- (args: GridMouseEventArgs) => {
252
- const tableBounds = tableWrapperRef.current?.getBoundingClientRect()
253
-
254
- if (args.kind === 'cell') {
255
- setHoveredHeader(null)
256
- const [col, row] = args.location
257
- const cell = getCellContent([col, row])
258
- if (!cell || cell.kind !== GridCellKind.Custom) {
259
- setHoveredCellData(null)
260
- return
261
- }
262
- const x = args.bounds.x - (tableBounds?.x ?? 0) + 10
263
- const y = args.bounds.y - (tableBounds?.y ?? 0) + args.bounds.height
264
- const hoverableRenderers = [countryArrayDataTableRenderer]
265
- const renderer = hoverableRenderers.find((r) => r.isMatch(cell))
266
- const data = cell.data
267
- const hoverOverlay = isHoverableRenderer(renderer) ? renderer.hoverOverlay : undefined
268
-
269
- setHoveredCellData({ x, y, data, hoverOverlay })
270
- return
271
- }
272
-
273
- if (args.kind === 'header') {
274
- setHoveredCellData(null)
275
- const [columnId] = args.location
276
- const x = args.bounds.x - (tableBounds?.x ?? 0) + 10
277
- const y = args.bounds.height
278
-
279
- setHoveredHeader({ columnId, x, y: y + (hasHeaderGroup ? 20 : 0) })
280
- return
281
- }
282
-
283
- setHoveredCellData(null)
284
- setHoveredHeader(null)
285
- },
286
- [getCellContent, hasHeaderGroup],
287
- )
288
-
289
- const onHeaderMenuClick = useCallback(
290
- (colIndex: number) => {
291
- if (!sortHandler) {
292
- // If sorting is not supported, do nothing on header click
293
- return
294
- }
295
- setManagedColumns((cols) =>
296
- cols.map((col, i) => {
297
- if (i !== colIndex) {
298
- return { ...col, activeSort: null, icon: undefined }
299
- }
300
- let newSort: 'asc' | 'desc' | null
301
- let icon: 'sortAscending' | 'sortDescending' | undefined = undefined
302
-
303
- switch (col.activeSort) {
304
- case null:
305
- newSort = 'asc'
306
- icon = 'sortAscending'
307
- break
308
- case 'asc':
309
- newSort = 'desc'
310
- icon = 'sortDescending'
311
- break
312
- case 'desc':
313
- newSort = null
314
- icon = undefined
315
- break
316
- }
317
-
318
- console.log(`Column ${col.id} sort state: ${col.activeSort} -> ${newSort}`)
319
-
320
- if (sortHandler) {
321
- sortHandler(col.id, newSort)
322
- }
323
- return { ...col, activeSort: newSort, icon }
324
- }),
325
- )
326
- },
327
- [sortHandler],
328
- )
329
-
330
- const onCellClicked = useCallback(
331
- ([col, row]: Item, event: CellClickedEventArgs) => {
332
- const rowData = getRowContent(row)
333
- const columnId = managedColumns[col].id
334
- const columnDefinition = managedColumns.find((col) => col.id === columnId)!
335
- if (columnDefinition.onClick) {
336
- columnDefinition.onClick(rowData, event)
337
- }
338
- },
339
- [getRowContent, managedColumns],
340
- )
341
-
342
- const onColumnResize = useCallback(
343
- ({ id }: { id?: string }, width: number) => {
344
- setManagedColumns((oldColumns) =>
345
- oldColumns.map((oldColumn) => (oldColumn.id === id ? { ...oldColumn, width } : oldColumn)),
346
- )
347
- },
348
- [setManagedColumns],
349
- )
350
- return (
351
- <ParentSize className="flex h-full min-h-[inherit] relative">
352
- {({ height }) => (
353
- <div
354
- className={`h-[${height}px] w-full flex flex-col relative`}
355
- ref={tableWrapperRef}
356
- data-test-id={__e2e_test_id__}
357
- >
358
- <DataEditor
359
- ref={dataEditorRef}
360
- freezeColumns={freezeColumns ?? 0}
361
- customRenderers={[
362
- linksTableRenderer,
363
- countryDataTableRenderer,
364
- countryArrayDataTableRenderer,
365
- favoriteDataTableRenderer,
366
- ]}
367
- columns={managedColumns}
368
- getCellContent={getCellContent}
369
- rows={data.length}
370
- onHeaderClicked={onHeaderMenuClick}
371
- onCellActivated={(cell) => console.log('cell activated', cell)}
372
- onItemHovered={onItemHovered}
373
- onHeaderMenuClick={onHeaderMenuClick}
374
- onColumnResize={onColumnResize}
375
- onCellClicked={onCellClicked}
376
- verticalBorder={false}
377
- headerIcons={{ sortAscending, sortDescending, noSort }}
378
- {...delegated}
379
- onCellContextMenu={onRightClick}
380
- theme={tableTheme}
381
- onVisibleRegionChanged={setVisibleRegion}
382
- rowHeight={22}
383
- headerHeight={22}
384
- smoothScrollX
385
- smoothScrollY
386
- />
387
-
388
- {hoveredHeader && managedColumns[hoveredHeader.columnId]?.tooltip && (
389
- <div
390
- style={
391
- {
392
- '--header-x': `${hoveredHeader.x}px`,
393
- '--header-y': `${hoveredHeader.y}px`,
394
- } as React.CSSProperties
395
- }
396
- className={
397
- 'absolute left-(--header-x) top-(--header-y) bg-background p-2 m-0.5 border border-(--discreet-border) shadow-md max-w-[400px]'
398
- }
399
- dangerouslySetInnerHTML={{ __html: managedColumns[hoveredHeader.columnId].tooltip ?? '' }}
400
- />
401
- )}
402
-
403
- {hoveredCellData?.hoverOverlay && (
404
- <div
405
- style={
406
- {
407
- '--header-x': `${hoveredCellData.x}px`,
408
- '--header-y': `${hoveredCellData.y}px`,
409
- } as React.CSSProperties
410
- }
411
- className={
412
- 'absolute left-(--header-x) top-(--header-y) bg-background p-2 m-0.5 border border-(--discreet-border) shadow-md max-w-[400px]'
413
- }
414
- >
415
- {hoveredCellData.hoverOverlay(hoveredCellData.data)}
416
- </div>
417
- )}
418
-
419
- {footer ? (
420
- <div className="flex absolute bottom-0 w-full items-center px-1 py-4 font-bold bg-background text-muted-foreground">
421
- {footer}
422
- </div>
423
- ) : null}
424
-
425
- {isLoading && <Loader label={isLoadingLabel} />}
426
- </div>
427
- )}
428
- </ParentSize>
429
- )
430
- }
431
-
432
- // CUSTOM ICONS ----------------------------------------------------------------
433
-
434
- const noSort = () => `
435
- <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor">
436
- <path transform="scale(0.75)" stroke-linecap="round" stroke-linejoin="round" d="M3 7.5 7.5 3m0 0L12 7.5M7.5 3v13.5m13.5 0L16.5 21m0 0L12 16.5m4.5 4.5V7.5" />
437
- </svg>
438
- `
439
-
440
- const sortAscending = () => `
441
- <svg width="15" height="15" viewBox="0 0 15 15" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M4 9H11L7.5 4.5L4 9Z" fill="currentColor"></path></svg>
442
-
443
- `
444
-
445
- const sortDescending = () => `
446
- <svg width="15" height="15" viewBox="0 0 15 15" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M4 6H11L7.5 10.5L4 6Z" fill="currentColor"></path></svg>
447
- `
@@ -1,77 +0,0 @@
1
- import type { CustomCell, CustomRenderer } from '@glideapps/glide-data-grid'
2
- import { GridCellKind } from '@glideapps/glide-data-grid'
3
-
4
- import { ReactNode } from 'react'
5
-
6
- import { APP_CONFIG } from '@lib/config'
7
-
8
- export type HoverableRenderer<T extends CustomCell = CustomCell> = {
9
- hoverOverlay?: (cellData: T['data']) => ReactNode
10
- } & CustomRenderer<T>
11
-
12
- export type CountryArrayCell = CustomCell<{
13
- kind: 'country-array-cell'
14
- countries: { id: string; label: string; hoverLabel?: string }[]
15
- }>
16
-
17
- const countryImageCache: Record<string, HTMLImageElement> = {}
18
-
19
- export const countryArrayDataTableRenderer: HoverableRenderer<CountryArrayCell> = {
20
- kind: GridCellKind.Custom,
21
- isMatch: (cell): cell is CountryArrayCell => (cell.data as { kind?: string }).kind === 'country-array-cell',
22
- draw: ({ ctx, rect, theme }, { data: { countries } }) => {
23
- const flagHeight = rect.height - theme.cellVerticalPadding * 2
24
- const flagWidth = flagHeight * 1.5 // flags are 3:2 aspect ratio
25
-
26
- let x = rect.x + theme.cellHorizontalPadding
27
- const y = rect.y + theme.cellVerticalPadding
28
-
29
- for (const { id, label } of countries) {
30
- const isUnknown = !id || id === ''
31
-
32
- let image: HTMLImageElement | undefined
33
-
34
- if (!isUnknown) {
35
- if (!countryImageCache[id]) {
36
- countryImageCache[id] = new Image()
37
- countryImageCache[id].src = `/flags/${id}.svg`
38
- countryImageCache[id].onload = () => {
39
- ctx.canvas.dispatchEvent(new Event('invalidate'))
40
- }
41
- }
42
- image = countryImageCache[id]
43
- }
44
-
45
- const circlepath = new Path2D()
46
- circlepath.roundRect(x, y, flagWidth, flagHeight, 2)
47
- ctx.save()
48
- ctx.clip(circlepath)
49
-
50
- if (isUnknown || !image || !image.complete || image.naturalHeight === 0 || APP_CONFIG.useTestAssets) {
51
- // Draw grey rectangle for unknown country
52
- ctx.fillStyle = '#cccccc'
53
- ctx.fillRect(x, y, flagWidth, flagHeight)
54
- } else {
55
- ctx.drawImage(image, x, y, flagWidth, flagHeight)
56
- }
57
-
58
- ctx.restore()
59
-
60
- ctx.save()
61
- ctx.font = `${theme.baseFontFull} ${theme.fontFamily}`
62
- ctx.fillStyle = theme.textDark
63
- ctx.fillText(label, x + flagWidth + 5, y + flagHeight / 2)
64
- ctx.restore()
65
-
66
- const labelWidth = ctx.measureText(label).width
67
- x += flagWidth + 5 + labelWidth + 10
68
- }
69
-
70
- return true
71
- },
72
- needsHover: true,
73
- hoverOverlay: (data) => {
74
- const { countries } = data
75
- return <>{countries.map((c) => c.hoverLabel || c.label).join(', ')}</>
76
- },
77
- }
@@ -1,56 +0,0 @@
1
- import type { CustomCell, CustomRenderer } from '@glideapps/glide-data-grid'
2
- import { GridCellKind } from '@glideapps/glide-data-grid'
3
-
4
- import { APP_CONFIG } from '@lib/config'
5
-
6
- export type CountryCell = CustomCell<{ kind: 'country'; name: string; code: string }>
7
-
8
- const countryImageCache: Record<string, HTMLImageElement> = {}
9
-
10
- export const countryDataTableRenderer: CustomRenderer<CountryCell> = {
11
- kind: GridCellKind.Custom,
12
- isMatch: (cell): cell is CountryCell => (cell.data as { kind?: string }).kind === 'country',
13
- draw: ({ ctx, rect, theme }, { data: { name, code } }) => {
14
- if (!code || code === '') {
15
- return false
16
- }
17
- const flagHeight = rect.height - theme.cellVerticalPadding * 2
18
- const flagWidth = flagHeight * 1.5 // flags are 3:2 aspect ratio
19
-
20
- const top = rect.y + theme.cellVerticalPadding
21
- const left = rect.x + theme.cellHorizontalPadding
22
-
23
- if (!countryImageCache[code]) {
24
- countryImageCache[code] = new Image()
25
- countryImageCache[code].src = `/flags/${code}.svg`
26
- countryImageCache[code].onload = () => {
27
- ctx.canvas.dispatchEvent(new Event('invalidate'))
28
- }
29
- }
30
-
31
- const image = countryImageCache[code]
32
-
33
- if (image.complete && image.naturalHeight !== 0 && !APP_CONFIG.useTestAssets) {
34
- const circlepath = new Path2D()
35
- circlepath.roundRect(left, top, flagWidth, flagHeight, 2)
36
-
37
- ctx.save()
38
- ctx.clip(circlepath)
39
- ctx.drawImage(image, left, top, flagWidth, flagHeight)
40
- ctx.restore()
41
- } else {
42
- ctx.save()
43
- ctx.fillStyle = theme.textLight
44
- ctx.fillRect(left, top, flagWidth, flagHeight)
45
- ctx.restore()
46
- }
47
-
48
- ctx.save()
49
- ctx.font = `${theme.baseFontFull} ${theme.fontFamily}`
50
- ctx.fillStyle = theme.textDark
51
- ctx.fillText(name, left + flagWidth + 5, top + flagHeight / 2)
52
- ctx.restore()
53
-
54
- return true
55
- },
56
- }
@@ -1,68 +0,0 @@
1
- import type { CustomCell, CustomRenderer } from '@glideapps/glide-data-grid'
2
- import { GridCellKind } from '@glideapps/glide-data-grid'
3
-
4
- export type FavoriteCell = CustomCell<{ kind: 'favorite'; isFavorite: boolean; isSelected?: boolean }>
5
-
6
- export const favoriteDataTableRenderer: CustomRenderer<FavoriteCell> = {
7
- kind: GridCellKind.Custom,
8
- isMatch: (cell): cell is FavoriteCell => (cell.data as { kind?: string }).kind === 'favorite',
9
- draw: ({ ctx, rect, theme }, { data: { isFavorite, isSelected } }) => {
10
- const size = Math.min(rect.width, rect.height) - theme.cellVerticalPadding * 2
11
- const centerX = rect.x + rect.width / 2
12
- const centerY = rect.y + rect.height / 2
13
- const scale = size / 24 // Lucide icons are typically designed on a 24x24 grid
14
-
15
- // Save the current context state
16
- ctx.save()
17
-
18
- // Center and scale the coordinate system
19
- ctx.translate(centerX, centerY)
20
- ctx.scale(scale, scale)
21
-
22
- // Draw the star path following Lucide.dev's star shape
23
- ctx.beginPath()
24
-
25
- // These points create a star path similar to Lucide's design
26
- // Star points in a 24x24 grid centered at 0,0
27
- const points = [
28
- [0, -10], // Top point
29
- [2.9, -3.1], // Upper right connecting point
30
- [10, -3.1], // Right point
31
- [3.8, 1.2], // Lower right connecting point
32
- [6.2, 8.2], // Lower right point
33
- [0, 4.4], // Bottom connecting point
34
- [-6.2, 8.2], // Lower left point
35
- [-3.8, 1.2], // Lower left connecting point
36
- [-10, -3.1], // Left point
37
- [-2.9, -3.1], // Upper left connecting point
38
- ]
39
-
40
- // Draw the star path
41
- ctx.moveTo(points[0][0], points[0][1])
42
- for (let i = 1; i < points.length; i++) {
43
- ctx.lineTo(points[i][0], points[i][1])
44
- }
45
- ctx.closePath()
46
-
47
- // Fill or just stroke based on favourite state
48
- if (isFavorite) {
49
- // Lucide uses a yellow/gold fill when active
50
- ctx.fillStyle = '#FFD700' // Gold color for filled star
51
- ctx.fill()
52
- ctx.lineWidth = 2
53
- ctx.strokeStyle = isSelected ? '#fb4b28' : '#D4AF37'
54
- } else {
55
- // When not favorited, only draw the outline
56
- ctx.lineWidth = isSelected ? 2 : 1 // Thinner line for cleaner look
57
- ctx.strokeStyle = isSelected ? '#fb4b28' : theme.textMedium
58
- }
59
-
60
- // Always draw the stroke
61
- ctx.stroke()
62
-
63
- // Restore the context state
64
- ctx.restore()
65
-
66
- return true
67
- },
68
- }