@postxl/generators 1.1.1 → 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 (161) hide show
  1. package/dist/frontend-core/frontend.generator.d.ts +0 -58
  2. package/dist/frontend-core/frontend.generator.js +6 -172
  3. package/dist/frontend-core/frontend.generator.js.map +1 -1
  4. package/dist/frontend-core/template/README.md +1 -1
  5. package/dist/frontend-core/template/src/components/admin/table-filter.tsx +1 -5
  6. package/dist/frontend-core/template/src/components/ui/color-mode-toggle/color-mode-toggle.tsx +10 -4
  7. package/dist/frontend-core/template/src/pages/dashboard/dashboard.page.tsx +2 -3
  8. package/dist/frontend-core/template/src/pages/error/default-error.page.tsx +1 -1
  9. package/dist/frontend-core/template/src/pages/error/not-found-error.page.tsx +1 -1
  10. package/dist/frontend-core/template/src/styles/styles.css +13 -1
  11. package/dist/frontend-core/template/tsconfig.json +2 -0
  12. package/dist/frontend-core/types/component.d.ts +1 -1
  13. package/dist/frontend-forms/generators/discriminated-union/fields.generator.js +4 -6
  14. package/dist/frontend-forms/generators/discriminated-union/fields.generator.js.map +1 -1
  15. package/dist/frontend-forms/generators/discriminated-union/inputs.generator.js +1 -1
  16. package/dist/frontend-forms/generators/discriminated-union/inputs.generator.js.map +1 -1
  17. package/dist/frontend-forms/generators/enum/inputs.generator.js +1 -1
  18. package/dist/frontend-forms/generators/enum/inputs.generator.js.map +1 -1
  19. package/dist/frontend-forms/generators/model/forms.generator.js +8 -12
  20. package/dist/frontend-forms/generators/model/forms.generator.js.map +1 -1
  21. package/dist/frontend-forms/generators/model/inputs.generator.js +2 -6
  22. package/dist/frontend-forms/generators/model/inputs.generator.js.map +1 -1
  23. package/dist/frontend-forms/template/src/components/ui/field/field.tsx +1 -4
  24. package/dist/frontend-tables/generators/model-table.generator.js +1 -5
  25. package/dist/frontend-tables/generators/model-table.generator.js.map +1 -1
  26. package/package.json +3 -2
  27. package/dist/frontend-core/template/src/components/ui/accordion/accordion.stories.tsx +0 -47
  28. package/dist/frontend-core/template/src/components/ui/accordion/accordion.tsx +0 -52
  29. package/dist/frontend-core/template/src/components/ui/admin-sidebar/admin-sidebar.tsx +0 -195
  30. package/dist/frontend-core/template/src/components/ui/alert/alert.stories.tsx +0 -61
  31. package/dist/frontend-core/template/src/components/ui/alert/alert.tsx +0 -45
  32. package/dist/frontend-core/template/src/components/ui/alert-dialog/alert-dialog.stories.tsx +0 -52
  33. package/dist/frontend-core/template/src/components/ui/alert-dialog/alert-dialog.tsx +0 -105
  34. package/dist/frontend-core/template/src/components/ui/avatar/avatar.stories.tsx +0 -30
  35. package/dist/frontend-core/template/src/components/ui/avatar/avatar.tsx +0 -39
  36. package/dist/frontend-core/template/src/components/ui/badge/badge.stories.tsx +0 -78
  37. package/dist/frontend-core/template/src/components/ui/badge/badge.tsx +0 -48
  38. package/dist/frontend-core/template/src/components/ui/breadcrumb/breadcrumb.stories.tsx +0 -67
  39. package/dist/frontend-core/template/src/components/ui/breadcrumb/breadcrumb.tsx +0 -85
  40. package/dist/frontend-core/template/src/components/ui/button/button.stories.tsx +0 -150
  41. package/dist/frontend-core/template/src/components/ui/button/button.tsx +0 -68
  42. package/dist/frontend-core/template/src/components/ui/calendar/calendar.stories.tsx +0 -160
  43. package/dist/frontend-core/template/src/components/ui/calendar/calendar.tsx +0 -293
  44. package/dist/frontend-core/template/src/components/ui/card/card.stories.tsx +0 -77
  45. package/dist/frontend-core/template/src/components/ui/card/card.tsx +0 -45
  46. package/dist/frontend-core/template/src/components/ui/card-hover/card-hover.stories.tsx +0 -29
  47. package/dist/frontend-core/template/src/components/ui/card-hover/card-hover.tsx +0 -28
  48. package/dist/frontend-core/template/src/components/ui/carousel/carousel.stories.tsx +0 -154
  49. package/dist/frontend-core/template/src/components/ui/carousel/carousel.tsx +0 -227
  50. package/dist/frontend-core/template/src/components/ui/checkbox/checkbox.stories.tsx +0 -106
  51. package/dist/frontend-core/template/src/components/ui/checkbox/checkbox.tsx +0 -88
  52. package/dist/frontend-core/template/src/components/ui/checkbox/shadcn-checkbox.stories.tsx +0 -90
  53. package/dist/frontend-core/template/src/components/ui/checkbox/shadcn-checkbox.tsx +0 -54
  54. package/dist/frontend-core/template/src/components/ui/collapse/collapse.stories.tsx +0 -52
  55. package/dist/frontend-core/template/src/components/ui/collapse/collapse.tsx +0 -9
  56. package/dist/frontend-core/template/src/components/ui/combobox/combobox.stories.tsx +0 -207
  57. package/dist/frontend-core/template/src/components/ui/combobox/combobox.tsx +0 -79
  58. package/dist/frontend-core/template/src/components/ui/command/command.stories.tsx +0 -186
  59. package/dist/frontend-core/template/src/components/ui/command/command.tsx +0 -165
  60. package/dist/frontend-core/template/src/components/ui/command-palette/command-palette.stories.tsx +0 -160
  61. package/dist/frontend-core/template/src/components/ui/command-palette/command-palette.tsx +0 -134
  62. package/dist/frontend-core/template/src/components/ui/content-frame/content-frame.stories.tsx +0 -198
  63. package/dist/frontend-core/template/src/components/ui/content-frame/content-frame.tsx +0 -100
  64. package/dist/frontend-core/template/src/components/ui/context-menu/context-menu.stories.tsx +0 -78
  65. package/dist/frontend-core/template/src/components/ui/context-menu/context-menu.tsx +0 -179
  66. package/dist/frontend-core/template/src/components/ui/data-grid/cell-variants/cell-variant-types.ts +0 -11
  67. package/dist/frontend-core/template/src/components/ui/data-grid/cell-variants/checkbox-cell.tsx +0 -116
  68. package/dist/frontend-core/template/src/components/ui/data-grid/cell-variants/date-cell.tsx +0 -157
  69. package/dist/frontend-core/template/src/components/ui/data-grid/cell-variants/gantt-cell.tsx +0 -82
  70. package/dist/frontend-core/template/src/components/ui/data-grid/cell-variants/long-text-cell.tsx +0 -180
  71. package/dist/frontend-core/template/src/components/ui/data-grid/cell-variants/multi-select-cell.tsx +0 -280
  72. package/dist/frontend-core/template/src/components/ui/data-grid/cell-variants/number-cell.tsx +0 -169
  73. package/dist/frontend-core/template/src/components/ui/data-grid/cell-variants/react-node-cell.tsx +0 -33
  74. package/dist/frontend-core/template/src/components/ui/data-grid/cell-variants/select-cell.tsx +0 -175
  75. package/dist/frontend-core/template/src/components/ui/data-grid/cell-variants/short-text-cell.tsx +0 -138
  76. package/dist/frontend-core/template/src/components/ui/data-grid/cell-variants/utils/gantt-timeline.tsx +0 -92
  77. package/dist/frontend-core/template/src/components/ui/data-grid/cell-variants/utils/gantt-timerange-picker.tsx +0 -330
  78. package/dist/frontend-core/template/src/components/ui/data-grid/data-grid-cell-wrapper.tsx +0 -212
  79. package/dist/frontend-core/template/src/components/ui/data-grid/data-grid-cell.tsx +0 -157
  80. package/dist/frontend-core/template/src/components/ui/data-grid/data-grid-column-header.tsx +0 -340
  81. package/dist/frontend-core/template/src/components/ui/data-grid/data-grid-context-menu.tsx +0 -271
  82. package/dist/frontend-core/template/src/components/ui/data-grid/data-grid-row.tsx +0 -123
  83. package/dist/frontend-core/template/src/components/ui/data-grid/data-grid-search.tsx +0 -211
  84. package/dist/frontend-core/template/src/components/ui/data-grid/data-grid-types.ts +0 -159
  85. package/dist/frontend-core/template/src/components/ui/data-grid/data-grid-utils.ts +0 -67
  86. package/dist/frontend-core/template/src/components/ui/data-grid/data-grid-view-menu.tsx +0 -360
  87. package/dist/frontend-core/template/src/components/ui/data-grid/data-grid.stories.tsx +0 -780
  88. package/dist/frontend-core/template/src/components/ui/data-grid/data-grid.tsx +0 -217
  89. package/dist/frontend-core/template/src/components/ui/data-grid/hooks/use-callback-ref.ts +0 -22
  90. package/dist/frontend-core/template/src/components/ui/data-grid/hooks/use-data-grid.tsx +0 -1892
  91. package/dist/frontend-core/template/src/components/ui/data-grid/hooks/use-debounced-callback.ts +0 -19
  92. package/dist/frontend-core/template/src/components/ui/data-grid/styles.css +0 -3
  93. package/dist/frontend-core/template/src/components/ui/data-table/context-menu-simple.tsx +0 -141
  94. package/dist/frontend-core/template/src/components/ui/data-table/data-table.stories.tsx +0 -146
  95. package/dist/frontend-core/template/src/components/ui/data-table/data-table.tsx +0 -447
  96. package/dist/frontend-core/template/src/components/ui/data-table/renderers/country-array-cell-renderer.tsx +0 -77
  97. package/dist/frontend-core/template/src/components/ui/data-table/renderers/country-cell-renderer.tsx +0 -56
  98. package/dist/frontend-core/template/src/components/ui/data-table/renderers/favorite-cell-renderer.tsx +0 -68
  99. package/dist/frontend-core/template/src/components/ui/data-table/renderers/links-cell-renderer.tsx +0 -205
  100. package/dist/frontend-core/template/src/components/ui/data-table/utils/columns.ts +0 -351
  101. package/dist/frontend-core/template/src/components/ui/data-table/utils/data-table.utils.ts +0 -49
  102. package/dist/frontend-core/template/src/components/ui/date-picker/date-picker.stories.tsx +0 -149
  103. package/dist/frontend-core/template/src/components/ui/date-picker/date-picker.tsx +0 -30
  104. package/dist/frontend-core/template/src/components/ui/dialog/dialog.stories.tsx +0 -80
  105. package/dist/frontend-core/template/src/components/ui/dialog/dialog.tsx +0 -134
  106. package/dist/frontend-core/template/src/components/ui/drawer/drawer.stories.tsx +0 -104
  107. package/dist/frontend-core/template/src/components/ui/drawer/drawer.tsx +0 -87
  108. package/dist/frontend-core/template/src/components/ui/dropdown-menu/dropdown-menu.stories.tsx +0 -168
  109. package/dist/frontend-core/template/src/components/ui/dropdown-menu/dropdown-menu.tsx +0 -225
  110. package/dist/frontend-core/template/src/components/ui/input/input.stories.tsx +0 -141
  111. package/dist/frontend-core/template/src/components/ui/input/input.tsx +0 -47
  112. package/dist/frontend-core/template/src/components/ui/label/label.stories.tsx +0 -41
  113. package/dist/frontend-core/template/src/components/ui/label/label.tsx +0 -20
  114. package/dist/frontend-core/template/src/components/ui/loader/loader.stories.tsx +0 -45
  115. package/dist/frontend-core/template/src/components/ui/loader/loader.tsx +0 -17
  116. package/dist/frontend-core/template/src/components/ui/mark-value-renderer/mark-value-renderer.stories.tsx +0 -114
  117. package/dist/frontend-core/template/src/components/ui/mark-value-renderer/mark-value-renderer.tsx +0 -48
  118. package/dist/frontend-core/template/src/components/ui/menubar/menu.stories.tsx +0 -134
  119. package/dist/frontend-core/template/src/components/ui/menubar/menubar.tsx +0 -208
  120. package/dist/frontend-core/template/src/components/ui/modal/modal.stories.tsx +0 -297
  121. package/dist/frontend-core/template/src/components/ui/modal/modal.tsx +0 -80
  122. package/dist/frontend-core/template/src/components/ui/navigation-menu/navigation-menu.stories.tsx +0 -213
  123. package/dist/frontend-core/template/src/components/ui/navigation-menu/navigation-menu.tsx +0 -142
  124. package/dist/frontend-core/template/src/components/ui/pagination/pagination.stories.tsx +0 -49
  125. package/dist/frontend-core/template/src/components/ui/pagination/pagination.tsx +0 -84
  126. package/dist/frontend-core/template/src/components/ui/popover/popover.stories.tsx +0 -82
  127. package/dist/frontend-core/template/src/components/ui/popover/popover.tsx +0 -55
  128. package/dist/frontend-core/template/src/components/ui/progress/progress.stories.tsx +0 -80
  129. package/dist/frontend-core/template/src/components/ui/progress/progress.tsx +0 -17
  130. package/dist/frontend-core/template/src/components/ui/radio-group/radio-group.stories.tsx +0 -154
  131. package/dist/frontend-core/template/src/components/ui/radio-group/radio-group.tsx +0 -68
  132. package/dist/frontend-core/template/src/components/ui/resizable/resizable.stories.tsx +0 -73
  133. package/dist/frontend-core/template/src/components/ui/resizable/resizeable.tsx +0 -38
  134. package/dist/frontend-core/template/src/components/ui/scroll-area/scroll-area.stories.tsx +0 -55
  135. package/dist/frontend-core/template/src/components/ui/scroll-area/scroll-area.tsx +0 -39
  136. package/dist/frontend-core/template/src/components/ui/select/select.stories.tsx +0 -297
  137. package/dist/frontend-core/template/src/components/ui/select/select.tsx +0 -227
  138. package/dist/frontend-core/template/src/components/ui/separator/separator.tsx +0 -21
  139. package/dist/frontend-core/template/src/components/ui/separator/seperator.stories.tsx +0 -25
  140. package/dist/frontend-core/template/src/components/ui/sheet/sheet.stories.tsx +0 -45
  141. package/dist/frontend-core/template/src/components/ui/sheet/sheet.tsx +0 -107
  142. package/dist/frontend-core/template/src/components/ui/skeleton/skeleton.stories.tsx +0 -26
  143. package/dist/frontend-core/template/src/components/ui/skeleton/skeleton.tsx +0 -7
  144. package/dist/frontend-core/template/src/components/ui/slider/slider.stories.tsx +0 -101
  145. package/dist/frontend-core/template/src/components/ui/slider/slider.tsx +0 -98
  146. package/dist/frontend-core/template/src/components/ui/spinner/spinner.stories.tsx +0 -19
  147. package/dist/frontend-core/template/src/components/ui/spinner/spinner.tsx +0 -21
  148. package/dist/frontend-core/template/src/components/ui/switch/switch.stories.tsx +0 -33
  149. package/dist/frontend-core/template/src/components/ui/switch/switch.tsx +0 -28
  150. package/dist/frontend-core/template/src/components/ui/tabs/tabs.stories.tsx +0 -215
  151. package/dist/frontend-core/template/src/components/ui/tabs/tabs.tsx +0 -70
  152. package/dist/frontend-core/template/src/components/ui/textarea/textarea.stories.tsx +0 -138
  153. package/dist/frontend-core/template/src/components/ui/textarea/textarea.tsx +0 -40
  154. package/dist/frontend-core/template/src/components/ui/toast/toast.mdx +0 -31
  155. package/dist/frontend-core/template/src/components/ui/toast/toast.stories.tsx +0 -89
  156. package/dist/frontend-core/template/src/components/ui/toggle/toggle.stories.tsx +0 -65
  157. package/dist/frontend-core/template/src/components/ui/toggle/toggle.tsx +0 -38
  158. package/dist/frontend-core/template/src/components/ui/toggle-group/toggle-group.stories.tsx +0 -85
  159. package/dist/frontend-core/template/src/components/ui/toggle-group/toggle-group.tsx +0 -54
  160. package/dist/frontend-core/template/src/components/ui/tooltip/tooltip.stories.tsx +0 -29
  161. 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
- }