gridColumns.tsx 8.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296
  1. // @ts-nocheck
  2. import { CalculatedColumn, RenderCellProps } from 'react-data-grid'
  3. import { DefaultValue } from '../components/common/DefaultValue'
  4. import { NullValue } from '../components/common/NullValue'
  5. import { BooleanEditor } from '../components/editor/BooleanEditor'
  6. import { DateTimeEditor } from '../components/editor/DateTimeEditor'
  7. import { JsonEditor } from '../components/editor/JsonEditor'
  8. import { NumberEditor } from '../components/editor/NumberEditor'
  9. import { SelectEditor } from '../components/editor/SelectEditor'
  10. import { TextEditor } from '../components/editor/TextEditor'
  11. import { TimeEditor, TimeWithTimezoneEditor } from '../components/editor/TimeEditor'
  12. import { BinaryFormatter } from '../components/formatter/BinaryFormatter'
  13. import { BooleanFormatter } from '../components/formatter/BooleanFormatter'
  14. import { DefaultFormatter } from '../components/formatter/DefaultFormatter'
  15. import { ForeignKeyFormatter } from '../components/formatter/ForeignKeyFormatter'
  16. import { JsonFormatter } from '../components/formatter/JsonFormatter'
  17. import { AddColumn } from '../components/grid/AddColumn'
  18. import { ColumnHeader } from '../components/grid/ColumnHeader'
  19. import { SelectColumn } from '../components/grid/SelectColumn'
  20. import {
  21. isPendingAddRow,
  22. type ColumnType,
  23. type SupaColumn,
  24. type SupaRow,
  25. type SupaTable,
  26. } from '../types'
  27. import {
  28. isArrayColumn,
  29. isBinaryColumn,
  30. isBoolColumn,
  31. isCiTextColumn,
  32. isDateColumn,
  33. isDateTimeColumn,
  34. isEnumColumn,
  35. isForeignKeyColumn,
  36. isJsonColumn,
  37. isNumericalColumn,
  38. isTextColumn,
  39. isTimeColumn,
  40. } from './types'
  41. import { COLUMN_MIN_WIDTH } from '@/components/grid/constants'
  42. export const ESTIMATED_CHARACTER_PIXEL_WIDTH = 9
  43. export function getGridColumns(
  44. table: SupaTable,
  45. options?: {
  46. tableId?: number
  47. editable?: boolean
  48. defaultWidth?: string | number
  49. onAddColumn?: () => void
  50. onExpandJSONEditor: (column: string, row: SupaRow) => void
  51. onExpandTextEditor: (column: string, row: SupaRow) => void
  52. }
  53. ): any[] {
  54. const columns = table.columns.map((x, idx) => {
  55. const columnType = getColumnType(x)
  56. const columnDefaultWidth = getColumnDefaultWidth(x)
  57. const columnWidthBasedOnName =
  58. (x.name.length + x.format.length) * ESTIMATED_CHARACTER_PIXEL_WIDTH
  59. const columnWidth = options?.defaultWidth
  60. ? options.defaultWidth
  61. : columnDefaultWidth < columnWidthBasedOnName
  62. ? columnWidthBasedOnName
  63. : columnDefaultWidth
  64. const columnDefinition: CalculatedColumn<SupaRow> = {
  65. key: x.name,
  66. name: x.name,
  67. idx: idx + 1,
  68. resizable: true,
  69. sortable: true,
  70. width: columnWidth,
  71. minWidth: COLUMN_MIN_WIDTH,
  72. frozen: false,
  73. renderHeaderCell: (props) => (
  74. <ColumnHeader
  75. {...props}
  76. columnType={columnType}
  77. isPrimaryKey={x.isPrimaryKey}
  78. isEncrypted={x.isEncrypted}
  79. format={x.format}
  80. foreignKey={x.foreignKey}
  81. comment={x.comment}
  82. />
  83. ),
  84. renderEditCell: options
  85. ? getCellEditor(
  86. x,
  87. columnType,
  88. options?.editable ?? false,
  89. options.onExpandJSONEditor,
  90. options.onExpandTextEditor
  91. )
  92. : undefined,
  93. renderCell: getCellRenderer(x, columnType, {
  94. tableId: options?.tableId,
  95. }),
  96. // [Next 18 Refactor] Double check if this is correct
  97. parent: undefined,
  98. level: 0,
  99. maxWidth: undefined,
  100. draggable: false,
  101. }
  102. return columnDefinition
  103. })
  104. const gridColumns = [SelectColumn, ...columns]
  105. if (options?.onAddColumn) {
  106. gridColumns.push(AddColumn)
  107. }
  108. return gridColumns
  109. }
  110. function getCellEditor(
  111. columnDefinition: SupaColumn,
  112. columnType: ColumnType,
  113. isEditable: boolean,
  114. onExpandJSONEditor: (column: string, row: any) => void,
  115. onExpandTextEditor: (column: string, row: any) => void
  116. ) {
  117. if (!isEditable) {
  118. if (['array', 'json'].includes(columnType)) {
  119. // eslint-disable-next-line react/display-name
  120. return (p: any) => (
  121. <JsonEditor {...p} isEditable={isEditable} onExpandEditor={onExpandJSONEditor} />
  122. )
  123. } else if (!['number', 'boolean'].includes(columnType)) {
  124. // eslint-disable-next-line react/display-name
  125. return (p: any) => (
  126. <TextEditor {...p} isEditable={isEditable} onExpandEditor={onExpandTextEditor} />
  127. )
  128. } else {
  129. return
  130. }
  131. }
  132. if (columnDefinition.isPrimaryKey || !columnDefinition.isUpdatable) {
  133. return
  134. }
  135. switch (columnType) {
  136. case 'boolean': {
  137. // eslint-disable-next-line react/display-name
  138. return (p: any) => <BooleanEditor {...p} isNullable={columnDefinition.isNullable} />
  139. }
  140. case 'date': {
  141. return DateTimeEditor('date', columnDefinition.isNullable || false)
  142. }
  143. case 'datetime': {
  144. return columnDefinition.format.endsWith('z')
  145. ? DateTimeEditor('datetimetz', columnDefinition.isNullable || false)
  146. : DateTimeEditor('datetime', columnDefinition.isNullable || false)
  147. }
  148. case 'time': {
  149. return columnDefinition.format.endsWith('z') ? TimeWithTimezoneEditor : TimeEditor
  150. }
  151. case 'enum': {
  152. const options = columnDefinition.enum!.map((x) => {
  153. return { label: x, value: x }
  154. })
  155. // eslint-disable-next-line react/display-name
  156. return (p: any) => (
  157. <SelectEditor {...p} options={options} isNullable={columnDefinition.isNullable} />
  158. )
  159. }
  160. case 'array':
  161. case 'json': {
  162. // eslint-disable-next-line react/display-name
  163. return (p: any) => <JsonEditor {...p} onExpandEditor={onExpandJSONEditor} />
  164. }
  165. case 'number': {
  166. return NumberEditor
  167. }
  168. case 'citext':
  169. case 'text': {
  170. // eslint-disable-next-line react/display-name
  171. return (p: any) => (
  172. <TextEditor
  173. {...p}
  174. isEditable={isEditable}
  175. isNullable={columnDefinition.isNullable}
  176. onExpandEditor={onExpandTextEditor}
  177. />
  178. )
  179. }
  180. default: {
  181. return undefined
  182. }
  183. }
  184. }
  185. function withPendingAddPlaceholders(
  186. Formatter: React.ComponentType<RenderCellProps<SupaRow, unknown>>,
  187. columnDef: SupaColumn
  188. ) {
  189. return function PendingAwareFormatter(props: RenderCellProps<SupaRow, unknown>) {
  190. const value = props.row[props.column.key]
  191. if (isPendingAddRow(props.row) && (value === undefined || value === null || value === '')) {
  192. if (value === null) {
  193. return <NullValue />
  194. }
  195. if (columnDef.defaultValue !== undefined || columnDef.isIdentity || columnDef.isGeneratable) {
  196. return <DefaultValue />
  197. }
  198. }
  199. return <Formatter {...props} />
  200. }
  201. }
  202. function getCellRenderer(
  203. columnDef: SupaColumn,
  204. columnType: ColumnType,
  205. metadata: { tableId?: number }
  206. ) {
  207. let formatter: React.ComponentType<RenderCellProps<SupaRow, unknown>>
  208. switch (columnType) {
  209. case 'boolean': {
  210. formatter = BooleanFormatter
  211. break
  212. }
  213. case 'foreign_key': {
  214. if (!columnDef.isUpdatable) {
  215. formatter = DefaultFormatter
  216. } else {
  217. // eslint-disable-next-line react/display-name
  218. formatter = (p: any) => <ForeignKeyFormatter {...p} tableId={metadata.tableId} />
  219. }
  220. break
  221. }
  222. case 'binary': {
  223. formatter = BinaryFormatter
  224. break
  225. }
  226. case 'json': {
  227. formatter = JsonFormatter
  228. break
  229. }
  230. default: {
  231. formatter = DefaultFormatter
  232. }
  233. }
  234. // Wrap all formatters to handle pending add row placeholders
  235. return withPendingAddPlaceholders(formatter, columnDef)
  236. }
  237. export function getColumnType(columnDef: SupaColumn): ColumnType {
  238. if (isForeignKeyColumn(columnDef)) {
  239. return 'foreign_key'
  240. } else if (isNumericalColumn(columnDef.dataType)) {
  241. return 'number'
  242. } else if (isArrayColumn(columnDef.dataType)) {
  243. return 'array'
  244. } else if (isJsonColumn(columnDef.dataType)) {
  245. return 'json'
  246. } else if (isTextColumn(columnDef.dataType)) {
  247. return 'text'
  248. } else if (isCiTextColumn(columnDef.format)) {
  249. return 'citext'
  250. } else if (isDateColumn(columnDef.format)) {
  251. return 'date'
  252. } else if (isTimeColumn(columnDef.format)) {
  253. return 'time'
  254. } else if (isDateTimeColumn(columnDef.format)) {
  255. return 'datetime'
  256. } else if (isBoolColumn(columnDef.dataType)) {
  257. return 'boolean'
  258. } else if (isEnumColumn(columnDef.dataType)) {
  259. return 'enum'
  260. } else if (isBinaryColumn(columnDef.dataType)) {
  261. return 'binary'
  262. } else return 'unknown'
  263. }
  264. export function getColumnDefaultWidth(columnDef: SupaColumn): number {
  265. if (isNumericalColumn(columnDef.dataType)) {
  266. return 120
  267. } else if (
  268. isDateTimeColumn(columnDef.format) ||
  269. isDateColumn(columnDef.format) ||
  270. isTimeColumn(columnDef.format)
  271. ) {
  272. return 150
  273. } else if (isBoolColumn(columnDef.dataType)) {
  274. return 120
  275. } else if (isEnumColumn(columnDef.dataType)) {
  276. return 150
  277. } else return 250
  278. }