Home / Function/ TableList() — supabase Function Reference

TableList() — supabase Function Reference

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

Entity Profile

Dependency Diagram

graph TD
  60b33a3e_c90b_00bb_64e3_60807774634b["TableList()"]
  41ffbcfa_1df7_ad96_f161_7269656b70f5["formatAllEntities()"]
  60b33a3e_c90b_00bb_64e3_60807774634b -->|calls| 41ffbcfa_1df7_ad96_f161_7269656b70f5
  style 60b33a3e_c90b_00bb_64e3_60807774634b fill:#6366f1,stroke:#818cf8,color:#fff

Relationship Graph

Source Code

apps/studio/components/interfaces/Database/Tables/TableList.tsx lines 75–563

export const TableList = ({
  onDuplicateTable,
  onAddTable = noop,
  onEditTable = noop,
  onDeleteTable = noop,
}: TableListProps) => {
  const router = useRouter()
  const { ref } = useParams()
  const { data: project } = useSelectedProjectQuery()

  const prefetchEditorTablePage = usePrefetchEditorTablePage()

  const { selectedSchema, setSelectedSchema } = useQuerySchemaState()

  const [filterString, setFilterString] = useQueryState('search', parseAsString.withDefault(''))
  const [visibleTypes, setVisibleTypes] = useState<string[]>(Object.values(ENTITY_TYPE))

  const { can: canUpdateTables } = useAsyncCheckPermissions(
    PermissionAction.TENANT_SQL_ADMIN_WRITE,
    'tables'
  )

  const {
    data: tables,
    error: tablesError,
    isError: isErrorTables,
    isPending: isLoadingTables,
    isSuccess: isSuccessTables,
  } = useTablesQuery(
    {
      projectRef: project?.ref,
      connectionString: project?.connectionString,
      schema: selectedSchema,
      sortByProperty: 'name',
      includeColumns: true,
    },
    {
      select(tables) {
        return filterString.length === 0
          ? tables
          : tables.filter((table) => table.name.toLowerCase().includes(filterString.toLowerCase()))
      },
    }
  )

  const {
    data: views,
    error: viewsError,
    isError: isErrorViews,
    isPending: isLoadingViews,
    isSuccess: isSuccessViews,
  } = useViewsQuery(
    {
      projectRef: project?.ref,
      connectionString: project?.connectionString,
      schema: selectedSchema,
    },
    {
      select(views) {
        return filterString.length === 0
          ? views
          : views.filter((view) => view.name.toLowerCase().includes(filterString.toLowerCase()))
      },
    }
  )

  const {
    data: materializedViews,
    error: materializedViewsError,
    isError: isErrorMaterializedViews,
    isPending: isLoadingMaterializedViews,
    isSuccess: isSuccessMaterializedViews,
  } = useMaterializedViewsQuery(
    {
      projectRef: project?.ref,
      connectionString: project?.connectionString,
      schema: selectedSchema,
    },
    {
      select(materializedViews) {
        return filterString.length === 0
          ? materializedViews
          : materializedViews.filter((view) =>
              view.name.toLowerCase().includes(filterString.toLowerCase())
            )
      },
    }
  )

  const {
    data: foreignTables,
    error: foreignTablesError,
    isError: isErrorForeignTables,
    isPending: isLoadingForeignTables,
    isSuccess: isSuccessForeignTables,
  } = useForeignTablesQuery(
    {
      projectRef: project?.ref,
      connectionString: project?.connectionString,
      schema: selectedSchema,
    },
    {
      select(foreignTables) {
        return filterString.length === 0
          ? foreignTables
          : foreignTables.filter((table) =>
              table.name.toLowerCase().includes(filterString.toLowerCase())
            )
      },
    }
  )

  const { data: publications } = useDatabasePublicationsQuery({
    projectRef: project?.ref,
    connectionString: project?.connectionString,
  })
  const realtimePublication = (publications ?? []).find(
    (publication) => publication.name === 'supabase_realtime'
  )

  const entities = formatAllEntities({ tables, views, materializedViews, foreignTables }).filter(
    (x) => visibleTypes.includes(x.type)
  )

  const { isSchemaLocked } = useIsProtectedSchema({ schema: selectedSchema })

  const error = tablesError || viewsError || materializedViewsError || foreignTablesError
  const isError = isErrorTables || isErrorViews || isErrorMaterializedViews || isErrorForeignTables
  const isLoading =
    isLoadingTables || isLoadingViews || isLoadingMaterializedViews || isLoadingForeignTables
  const isSuccess =
    isSuccessTables && isSuccessViews && isSuccessMaterializedViews && isSuccessForeignTables

  const formatTooltipText = (entityType: string) => {
    const text =
      Object.entries(ENTITY_TYPE)
        .find(([, value]) => value === entityType)?.[0]
        ?.toLowerCase()
        ?.split('_')
        ?.join(' ') || ''
    // Return sentence case (capitalize first letter only)
    return text.charAt(0).toUpperCase() + text.slice(1)
  }

  return (
    <div className="flex flex-col gap-y-4">
      <div className="flex flex-col lg:flex-row lg:items-center gap-2 flex-wrap">
        <div className="flex gap-2 items-center">
          <SchemaSelector
            className="flex-grow lg:flex-grow-0 w-[180px]"
            size="tiny"
            showError={false}
            selectedSchemaName={selectedSchema}
            onSelectSchema={setSelectedSchema}
          />
          <Popover_Shadcn_>
            <PopoverTrigger_Shadcn_ asChild>
              <Button
                size="tiny"
                type={visibleTypes.length !== 5 ? 'default' : 'dashed'}
                className="px-1"
                icon={<Filter />}
              />
            </PopoverTrigger_Shadcn_>
            <PopoverContent_Shadcn_ className="p-0 w-56" side="bottom" align="center">
              <div className="px-3 pt-3 pb-2 flex flex-col gap-y-2">
                <p className="text-xs">Show entity types</p>
                <div className="flex flex-col">
                  {Object.entries(ENTITY_TYPE).map(([key, value]) => (
                    <div key={key} className="group flex items-center justify-between py-0.5">
                      <div className="flex items-center gap-x-2">
                        <Checkbox_Shadcn_
                          id={key}
                          name={key}
                          checked={visibleTypes.includes(value)}
                          onCheckedChange={() => {
                            if (visibleTypes.includes(value)) {
                              setVisibleTypes(visibleTypes.filter((y) => y !== value))
                            } else {
                              setVisibleTypes(visibleTypes.concat([value]))
                            }
                          }}
                        />
                        <Label_Shadcn_ htmlFor={key} className="capitalize text-xs">
                          {key.toLowerCase().replace('_', ' ')}
                        </Label_Shadcn_>
                      </div>
                      <Button
                        size="tiny"
                        type="default"
                        onClick={() => setVisibleTypes([value])}
                        className="transition opacity-0 group-hover:opacity-100 h-auto px-1 py-0.5"
                      >
                        Select only
                      </Button>
                    </div>
                  ))}
                </div>
              </div>
            </PopoverContent_Shadcn_>
          </Popover_Shadcn_>
        </div>
        <div className="flex flex-grow justify-between gap-2 items-center">
          <Input
            size="tiny"
            className="flex-grow lg:flex-grow-0 w-52"
            placeholder="Search for a table"
            value={filterString}
            onChange={(e) => setFilterString(e.target.value)}
            icon={<Search />}
          />

          {!isSchemaLocked && (
            <ButtonTooltip
              className="w-auto ml-auto"
              icon={<Plus />}
              disabled={!canUpdateTables}
              onClick={() => onAddTable()}
              tooltip={{
                content: {
                  side: 'bottom',
                  text: !canUpdateTables
                    ? 'You need additional permissions to create tables'
                    : undefined,
                },
              }}
            >
              New table
            </ButtonTooltip>
          )}
        </div>
      </div>

      {isSchemaLocked && <ProtectedSchemaWarning schema={selectedSchema} entity="tables" />}

      {isLoading && <GenericSkeletonLoader />}

      {isError && <AlertError error={error} subject="Failed to retrieve tables" />}

      {isSuccess && (
        <div className="w-full">
          <Card>
            <Table>
              <TableHeader>
                <TableRow>
                  <TableHead key="icon" className="!px-0" />
                  <TableHead key="name">Name</TableHead>
                  <TableHead key="description" className="hidden lg:table-cell">
                    Description
                  </TableHead>
                  <TableHead key="rows" className="hidden text-right xl:table-cell">
                    Rows (Estimated)
                  </TableHead>
                  <TableHead key="size" className="hidden text-right xl:table-cell">
                    Size (Estimated)
                  </TableHead>
                  <TableHead key="realtime" className="hidden xl:table-cell text-right">
                    Realtime Enabled
                  </TableHead>
                  <TableHead key="buttons"></TableHead>
                </TableRow>
              </TableHeader>
              <TableBody>
                <>
                  {entities.length === 0 && filterString.length === 0 && (
                    <TableRow key={selectedSchema}>
                      <TableCell colSpan={7}>
                        {visibleTypes.length === 0 ? (
                          <>
                            <p className="text-sm text-foreground">
                              Please select at least one entity type to filter with
                            </p>
                            <p className="text-sm text-foreground-light">
                              There are currently no results based on the filter that you have
                              applied
                            </p>
                          </>
                        ) : (
                          <>
                            <p className="text-sm text-foreground">No tables created yet</p>
                            <p className="text-sm text-foreground-light">
                              There are no{' '}
                              {visibleTypes.length === 5
                                ? 'tables'
                                : visibleTypes.length === 1
                                  ? `${formatTooltipText(visibleTypes[0])}s`
                                  : `${visibleTypes
                                      .slice(0, -1)
                                      .map((x) => `${formatTooltipText(x)}s`)
                                      .join(
                                        ', '
                                      )}, and ${formatTooltipText(visibleTypes[visibleTypes.length - 1])}s`}{' '}
                              found in the schema "{selectedSchema}"
                            </p>
                          </>
                        )}
                      </TableCell>
                    </TableRow>
                  )}
                  {entities.length === 0 && filterString.length > 0 && (
                    <TableRow key={selectedSchema}>
                      <TableCell colSpan={7}>
                        <p className="text-sm text-foreground">No results found</p>
                        <p className="text-sm text-foreground-light">
                          Your search for "{filterString}" did not return any results
                        </p>
                      </TableCell>
                    </TableRow>
                  )}
                  {entities.length > 0 &&
                    entities.map((x) => (
                      <TableRow key={x.id}>
                        <TableCell className="!pl-5 !pr-1">
                          <Tooltip>
                            <TooltipTrigger className="cursor-default">
                              {/* [Alaister]: EntityTypeIcon supports PARTITIONED_TABLE, but formatAllEntities
                                  doesn't distinguish between tables and partitioned tables yet.
                                  Once the endpoint/formatAllEntities is updated to include partitioned tables,
                                  EntityTypeIcon will automatically style them correctly. */}
                              <EntityTypeIcon type={x.type} />
                            </TooltipTrigger>
                            <TooltipContent side="bottom">
                              {formatTooltipText(x.type)}
                            </TooltipContent>
                          </Tooltip>
                        </TableCell>
                        <TableCell>
                          {/* only show tooltips if required, to reduce noise */}
                          {x.name.length > 20 ? (
                            <Tooltip disableHoverableContent={true}>
                              <TooltipTrigger
                                asChild
                                className="max-w-[95%] overflow-hidden text-ellipsis whitespace-nowrap"
                              >
                                <p>{x.name}</p>
                              </TooltipTrigger>

                              <TooltipContent side="bottom">{x.name}</TooltipContent>
                            </Tooltip>
                          ) : (
                            <p>{x.name}</p>
                          )}
                        </TableCell>
                        <TableCell className="hidden lg:table-cell ">
                          {x.comment !== null ? (
                            <span className="lg:max-w-48 truncate inline-block" title={x.comment}>
                              {x.comment}
                            </span>
                          ) : (
                            <p className="text-border-stronger">No description</p>
                          )}
                        </TableCell>
                        <TableCell className="hidden text-right xl:table-cell">
                          {x.rows !== undefined ? x.rows.toLocaleString() : '-'}
                        </TableCell>
                        <TableCell className="hidden text-right xl:table-cell">
                          {x.size !== undefined ? (
                            <code className="text-code-inline">{x.size}</code>
                          ) : (
                            '-'
                          )}
                        </TableCell>
                        <TableCell className="hidden xl:table-cell text-center">
                          {(realtimePublication?.tables ?? []).find(
                            (table) => table.id === x.id
                          ) ? (
                            <div className="flex justify-end">
                              <Check size={18} strokeWidth={2} className="text-brand" />
                            </div>
                          ) : (
                            <div className="flex justify-end">
                              <X size={18} strokeWidth={2} className="text-foreground-lighter" />
                            </div>
                          )}
                        </TableCell>
                        <TableCell>
                          <div className="flex justify-end gap-2">
                            <Button
                              asChild
                              type="default"
                              iconRight={<Columns size={14} className="text-foreground-light" />}
                              className="whitespace-nowrap hover:border-muted"
                              style={{ paddingTop: 3, paddingBottom: 3 }}
                            >
                              <Link href={`/project/${ref}/database/tables/${x.id}`}>
                                {x.columns.length} columns
                              </Link>
                            </Button>

                            {!isSchemaLocked && (
                              <DropdownMenu>
                                <DropdownMenuTrigger asChild>
                                  <Button type="default" className="px-1" icon={<MoreVertical />} />
                                </DropdownMenuTrigger>
                                <DropdownMenuContent side="bottom" align="end" className="w-40">
                                  <DropdownMenuItem
                                    className="flex items-center space-x-2"
                                    onClick={() =>
                                      router.push(
                                        buildTableEditorUrl({
                                          projectRef: project?.ref,
                                          tableId: x.id,
                                          schema: x.schema,
                                        })
                                      )
                                    }
                                    onMouseEnter={() =>
                                      prefetchEditorTablePage({
                                        id: x.id ? String(x.id) : undefined,
                                      })
                                    }
                                  >
                                    <Eye size={12} />
                                    <p>View in Table Editor</p>
                                  </DropdownMenuItem>

                                  {x.type === ENTITY_TYPE.TABLE && (
                                    <>
                                      <DropdownMenuSeparator />
                                      <DropdownMenuItemTooltip
                                        className="gap-x-2"
                                        disabled={!canUpdateTables}
                                        onClick={() => {
                                          if (canUpdateTables) onEditTable(x)
                                        }}
                                        tooltip={{
                                          content: {
                                            side: 'left',
                                            text: 'You need additional permissions to edit this table',
                                          },
                                        }}
                                      >
                                        <Edit size={12} />
                                        <p>Edit table</p>
                                      </DropdownMenuItemTooltip>
                                      <DropdownMenuItemTooltip
                                        key="duplicate-table"
                                        className="gap-x-2"
                                        disabled={!canUpdateTables}
                                        onClick={() => {
                                          if (canUpdateTables) onDuplicateTable(x)
                                        }}
                                        tooltip={{
                                          content: {
                                            side: 'left',
                                            text: 'You need additional permissions to duplicate tables',
                                          },
                                        }}
                                      >
                                        <Copy size={12} />
                                        <span>Duplicate Table</span>
                                      </DropdownMenuItemTooltip>
                                      <DropdownMenuSeparator />
                                      <DropdownMenuItemTooltip
                                        disabled={!canUpdateTables || isSchemaLocked}
                                        className="gap-x-2"
                                        onClick={() => {
                                          if (canUpdateTables && !isSchemaLocked) {
                                            onDeleteTable({ ...x, schema: selectedSchema })
                                          }
                                        }}
                                        tooltip={{
                                          content: {
                                            side: 'left',
                                            text: 'You need additional permissions to delete tables',
                                          },
                                        }}
                                      >
                                        <Trash size={12} />
                                        <p>Delete table</p>
                                      </DropdownMenuItemTooltip>
                                    </>
                                  )}
                                </DropdownMenuContent>
                              </DropdownMenu>
                            )}
                          </div>
                        </TableCell>
                      </TableRow>
                    ))}
                </>
              </TableBody>
            </Table>
          </Card>
        </div>
      )}
    </div>
  )
}

Subdomains

Frequently Asked Questions

What does TableList() do?
TableList() is a function in the supabase codebase.
What does TableList() call?
TableList() calls 1 function(s): formatAllEntities.

Analyze Your Own Codebase

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

Try Supermodel Free