Home / Function/ Grid() — supabase Function Reference

Grid() — supabase Function Reference

Architecture documentation for the Grid() function in Grid.tsx from the supabase codebase.

Entity Profile

Dependency Diagram

graph TD
  195c5181_2e45_3007_560a_5b290d5d2777["Grid()"]
  a47503db_fa39_e27f_a3b4_341051a9ae62["useTableFilter()"]
  195c5181_2e45_3007_560a_5b290d5d2777 -->|calls| a47503db_fa39_e27f_a3b4_341051a9ae62
  13c29bbf_8511_5da2_f728_9f9db5e200c0["useOnRowsChange()"]
  195c5181_2e45_3007_560a_5b290d5d2777 -->|calls| 13c29bbf_8511_5da2_f728_9f9db5e200c0
  style 195c5181_2e45_3007_560a_5b290d5d2777 fill:#6366f1,stroke:#818cf8,color:#fff

Relationship Graph

Source Code

apps/studio/components/grid/components/grid/Grid.tsx lines 45–360

    (
      {
        width,
        height,
        containerClass,
        gridClass,
        rowClass,
        rows,
        error,
        isDisabled = false,
        isLoading,
        isSuccess,
        isError,
      },
      ref: Ref<DataGridHandle> | undefined
    ) => {
      const newFilterBarEnabled = useIsTableFilterBarEnabled()

      const tableEditorSnap = useTableEditorStateSnapshot()
      const snap = useTableEditorTableStateSnapshot()
      const { filters: oldFilters, clearFilters: clearOldFilters } = useTableFilter()
      const { filters: newFilters, clearFilters: clearNewFilters } = useTableFilterNew()

      const { data: org } = useSelectedOrganizationQuery()
      const { data: project } = useSelectedProjectQuery()

      const onRowsChange = useOnRowsChange(rows)

      function onSelectedRowsChange(selectedRows: Set<number>) {
        snap.setSelectedRows(selectedRows)
      }

      const selectedCellRef = useRef<{
        rowIdx: number
        row: SupaRow
        column: CalculatedColumn<SupaRow, unknown>
      } | null>(null)

      function onSelectedCellChange(args: {
        rowIdx: number
        row: SupaRow
        column: CalculatedColumn<SupaRow, unknown>
      }) {
        selectedCellRef.current = args
        snap.setSelectedCellPosition({ idx: args.column.idx, rowIdx: args.rowIdx })
      }

      const page = snap.page
      const table = snap.table
      const tableEntityType = snap.originalTable?.entity_type
      const isForeignTable = tableEntityType === ENTITY_TYPE.FOREIGN_TABLE
      const isTableEmpty = (rows ?? []).length === 0

      const { mutate: sendEvent } = useSendEventMutation()

      const {
        isValidFile: isValidFileDraggedOver,
        isDraggedOver,
        onDragOver,
        onFileDrop,
      } = useCsvFileDrop({
        enabled: isTableEmpty && !isForeignTable,
        onFileDropped: (file) => tableEditorSnap.onImportData(valtioRef(file)),
        onTelemetryEvent: (eventName) => {
          sendEvent({
            action: eventName,
            groups: {
              project: project?.ref ?? 'Unknown',
              organization: org?.slug ?? 'Unknown',
            },
          })
        },
      })

      const { data } = useForeignKeyConstraintsQuery({
        projectRef: project?.ref,
        connectionString: project?.connectionString,
        schema: table?.schema ?? undefined,
      })

      function getColumnForeignKey(columnName: string) {
        const { targetTableSchema, targetTableName, targetColumnName } =
          table?.columns.find((x) => x.name == columnName)?.foreignKey ?? {}

        const fk = data?.find(
          (key) =>
            key.source_schema === table?.schema &&
            key.source_table === table?.name &&
            key.source_columns.includes(columnName) &&
            key.target_schema === targetTableSchema &&
            key.target_table === targetTableName &&
            key.target_columns.includes(targetColumnName ?? '')
        )

        return fk !== undefined ? formatForeignKeys([fk])[0] : undefined
      }

      function onRowDoubleClick(row: SupaRow, column: { name: string }) {
        const foreignKey = getColumnForeignKey(column.name)

        if (foreignKey) {
          tableEditorSnap.onEditForeignKeyColumnValue({
            foreignKey,
            row,
            column: column as unknown as PostgresColumn,
          })
        }
      }

      const removeAllFilters = useCallback(() => {
        if (newFilterBarEnabled) {
          clearNewFilters()
        } else {
          clearOldFilters()
        }
      }, [clearOldFilters, clearNewFilters, newFilterBarEnabled])

      const filters = useMemo(() => {
        if (newFilterBarEnabled) {
          return newFilters
        } else {
          return oldFilters
        }
      }, [newFilters, oldFilters, newFilterBarEnabled])

      // Compute columns with cellClass for dirty cells
      // This needs to be computed at render time so it reacts to operation queue changes
      const columnsWithDirtyCellClass = useMemo(() => {
        const primaryKeys = isTableLike(snap.originalTable) ? snap.originalTable.primary_keys : []
        const pendingOperations = tableEditorSnap.operationQueue.operations

        // If no pending operations, return columns as-is
        if (pendingOperations.length === 0) {
          return snap.gridColumns as CalculatedColumn<SupaRow, unknown>[]
        }

        return (snap.gridColumns as CalculatedColumn<SupaRow, unknown>[]).map((col) => {
          // Skip special columns like select column
          if (col.key === 'select-row' || col.key === 'add-column') {
            return col
          }

          return {
            ...col,
            cellClass: (row: SupaRow) => {
              // Build row identifiers from primary keys
              const rowIdentifiers: Record<string, unknown> = {}
              for (const pk of primaryKeys) {
                rowIdentifiers[pk.name] = row[pk.name]
              }

              // Check if this cell has pending changes
              const isDirty = tableEditorSnap.hasPendingCellChange(
                snap.table.id,
                rowIdentifiers,
                col.key
              )
              return isDirty ? 'rdg-cell--dirty' : undefined
            },
          }
        })
      }, [snap.gridColumns, snap.originalTable, snap.table.id, tableEditorSnap])

      // Compute rowClass function to style pending add/delete rows
      const computedRowClass = useMemo(() => {
        return (row: SupaRow) => {
          const classes: string[] = []

          // Call the original rowClass if provided
          if (rowClass) {
            const originalClass = rowClass(row)
            if (originalClass) {
              classes.push(originalClass)
            }
          }
          if (isPendingAddRow(row)) {
            classes.push('rdg-row--added')
          }
          if (isPendingDeleteRow(row)) {
            classes.push('rdg-row--deleted')
          }

          return classes.length > 0 ? classes.join(' ') : undefined
        }
      }, [rowClass])

      return (
        <div
          data-testid="table-editor-grid-container"
          className={cn('flex flex-col relative transition-colors', containerClass)}
          style={{ width: width || '100%', height: height || '50vh' }}
          onDragOver={onDragOver}
          onDragLeave={onDragOver}
          onDrop={onFileDrop}
        >
          {/* Render no rows fallback outside of the DataGrid */}
          {(rows ?? []).length === 0 && (
            <div
              className={cn(
                'absolute w-full inset-0 flex flex-col items-center justify-center p-2 z-[1] pointer-events-none',
                isTableEmpty && isDraggedOver && 'border-2 border-dashed',
                isValidFileDraggedOver ? 'border-brand-600' : 'border-destructive-600'
              )}
            >
              {isLoading && !isDisabled && (
                <GenericSkeletonLoader className="w-full top-9 absolute p-2" />
              )}

              {isError && <GridError error={error} />}

              {isSuccess && (
                <>
                  {page > 1 ? (
                    <div className="flex flex-col items-center justify-center">
                      <p className="text-sm text-light">This page does not have any data</p>
                      <div className="flex items-center space-x-2 mt-4">
                        <Button
                          type="default"
                          className="pointer-events-auto"
                          onClick={() => snap.setPage(1)}
                        >
                          Head back to first page
                        </Button>
                      </div>
                    </div>
                  ) : (filters ?? []).length === 0 ? (
                    <div className="flex flex-col items-center justify-center">
                      <p className="text-sm text-light pointer-events-auto">
                        {isDraggedOver ? (
                          isValidFileDraggedOver ? (
                            'Drop your CSV file here'
                          ) : (
                            <span className="text-destructive">Only CSV files are accepted</span>
                          )
                        ) : (
                          'This table is empty'
                        )}
                      </p>
                      {tableEntityType === ENTITY_TYPE.FOREIGN_TABLE ? (
                        <div className="flex items-center space-x-2 mt-4">
                          <p className="text-sm text-light pointer-events-auto">
                            This table is a foreign table. Add data to the connected source to get
                            started.
                          </p>
                        </div>
                      ) : (
                        !isDraggedOver && (
                          <div className="flex flex-col items-center gap-4 mt-4">
                            <Button
                              type="default"
                              className="pointer-events-auto"
                              onClick={() => {
                                tableEditorSnap.onImportData()
                                sendEvent({
                                  action: 'import_data_button_clicked',
                                  properties: { tableType: 'Existing Table' },
                                  groups: {
                                    project: project?.ref ?? 'Unknown',
                                    organization: org?.slug ?? 'Unknown',
                                  },
                                })
                              }}
                            >
                              Import data from CSV
                            </Button>
                            <p className="text-xs text-foreground-light pointer-events-auto">
                              or drag and drop a CSV file here
                            </p>
                          </div>
                        )
                      )}
                    </div>
                  ) : (
                    <div className="flex flex-col items-center justify-center">
                      <p className="text-sm text-light pointer-events-auto">
                        The filters applied have returned no results from this table
                      </p>
                      <div className="flex items-center space-x-2 mt-4">
                        <Button
                          type="default"
                          className="pointer-events-auto"
                          onClick={() => removeAllFilters()}
                        >
                          Remove all filters
                        </Button>
                      </div>
                    </div>
                  )}
                </>
              )}
            </div>
          )}

          <DataGrid
            ref={ref}
            className={`${gridClass} flex-grow`}
            rowClass={computedRowClass}
            columns={columnsWithDirtyCellClass}
            rows={rows ?? []}
            renderers={{ renderRow: RowRenderer }}
            rowKeyGetter={rowKeyGetter}
            selectedRows={snap.selectedRows}
            onColumnResize={snap.updateColumnSize}
            onRowsChange={onRowsChange}
            onSelectedCellChange={onSelectedCellChange}
            onSelectedRowsChange={onSelectedRowsChange}
            onCellDoubleClick={(props) => {
              if (typeof props.column.name === 'string') {
                onRowDoubleClick(props.row, { name: props.column.name })
              }
            }}
            onCellKeyDown={handleCopyCell}
          />
        </div>
      )
    }

Subdomains

Frequently Asked Questions

What does Grid() do?
Grid() is a function in the supabase codebase.
What does Grid() call?
Grid() calls 2 function(s): useOnRowsChange, useTableFilter.

Analyze Your Own Codebase

Get architecture documentation, dependency graphs, and domain analysis for your codebase in minutes.

Try Supermodel Free