Home / Function/ JsonEditor() — supabase Function Reference

JsonEditor() — supabase Function Reference

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

Entity Profile

Dependency Diagram

graph TD
  50bc62ef_1d39_0ed0_7e0c_1bec2d8d5b85["JsonEditor()"]
  ba33b09c_bc18_30d6_2ff3_8b87e531dcd1["tryFormatInitialValue()"]
  50bc62ef_1d39_0ed0_7e0c_1bec2d8d5b85 -->|calls| ba33b09c_bc18_30d6_2ff3_8b87e531dcd1
  b8f814ec_f6bc_2a0a_e400_fd5df18b3190["verifyJSON()"]
  50bc62ef_1d39_0ed0_7e0c_1bec2d8d5b85 -->|calls| b8f814ec_f6bc_2a0a_e400_fd5df18b3190
  style 50bc62ef_1d39_0ed0_7e0c_1bec2d8d5b85 fill:#6366f1,stroke:#818cf8,color:#fff

Relationship Graph

Source Code

apps/studio/components/grid/components/editor/JsonEditor.tsx lines 47–229

export const JsonEditor = <TRow, TSummaryRow = unknown>({
  row,
  column,
  isEditable = true,
  onRowChange,
  onExpandEditor,
}: JsonEditorProps<TRow, TSummaryRow>) => {
  const { id: _id } = useParams()
  const id = _id ? Number(_id) : undefined
  const { data: project } = useSelectedProjectQuery()

  const { data: selectedTable } = useTableEditorQuery({
    projectRef: project?.ref,
    connectionString: project?.connectionString,
    id,
  })

  const rawInitialValue = row[column.key as keyof TRow] as unknown
  const initialValue =
    rawInitialValue === null || rawInitialValue === undefined || typeof rawInitialValue === 'string'
      ? rawInitialValue
      : JSON.stringify(rawInitialValue)

  const jsonString = prettifyJSON(initialValue ? tryFormatInitialValue(initialValue) : '')

  const isTruncated = isValueTruncated(initialValue)
  const [isPopoverOpen, setIsPopoverOpen] = useState(true)
  const [value, setValue] = useState<string | null>(jsonString)

  const { mutate: getCellValue, isPending, isSuccess } = useGetCellValueMutation()

  const loadFullValue = () => {
    if (selectedTable === undefined || project === undefined || !isTableLike(selectedTable)) return
    if (selectedTable.primary_keys.length === 0) {
      return toast('Unable to load value as table has no primary keys')
    }

    const pkMatch = selectedTable.primary_keys.reduce((a, b) => {
      return { ...a, [b.name]: (row as any)[b.name] }
    }, {})

    getCellValue(
      {
        table: { schema: selectedTable.schema, name: selectedTable.name },
        column: column.name as string,
        pkMatch,
        projectRef: project?.ref,
        connectionString: project?.connectionString,
      },
      {
        onSuccess: (data) => {
          setValue(JSON.stringify(data))
        },
      }
    )
  }

  const cancelChanges = useCallback(() => {
    if (isEditable) onRowChange(row, true)
    setIsPopoverOpen(false)
  }, [])

  const saveChanges = useCallback(
    (newValue: string | null) => {
      const updatedValue = newValue !== null ? removeJSONTrailingComma(newValue) : newValue
      if (updatedValue !== value) {
        commitChange(newValue)
      } else {
        setIsPopoverOpen(false)
      }
    },
    [isSuccess]
  )

  const onChange = (_value: string | undefined) => {
    if (!isEditable) return
    if (!_value || _value == '') setValue(null)
    else setValue(_value)
  }

  const onSelectExpand = () => {
    cancelChanges()
    onExpandEditor(column.key, {
      ...row,
      [column.key]: tryParseJson(value) || (row as any)[column.key],
    })
  }

  const commitChange = (newValue: string | null) => {
    if (!isEditable) return

    if (!newValue) {
      onRowChange({ ...row, [column.key]: null }, true)
      setIsPopoverOpen(false)
    } else if (verifyJSON(newValue)) {
      const jsonValue = JSON.parse(newValue)
      onRowChange({ ...row, [column.key]: jsonValue }, true)
      setIsPopoverOpen(false)
    } else {
      toast.error('Please enter a valid JSON')
    }
  }

  return (
    <Popover
      open={isPopoverOpen}
      side="bottom"
      align="start"
      sideOffset={-35}
      className="rounded-none"
      overlay={
        isTruncated && !isSuccess ? (
          <div
            style={{ width: `${column.width}px` }}
            className="flex items-center justify-center flex-col relative"
          >
            <MonacoEditor
              readOnly
              onChange={() => {}}
              width={`${column.width}px`}
              value={value ?? ''}
              language="markdown"
            />
            <TruncatedWarningOverlay isLoading={isPending} loadFullValue={loadFullValue} />
          </div>
        ) : (
          <BlockKeys value={value} onEscape={cancelChanges} onEnter={saveChanges}>
            <MonacoEditor
              width={`${column.width}px`}
              value={value ?? ''}
              language="json"
              readOnly={!isEditable}
              onChange={onChange}
            />
            <div className="flex items-start justify-between p-2 bg-surface-200 gap-x-2">
              {isEditable && (
                <div className="space-y-1">
                  <div className="flex items-center space-x-2">
                    <div className="px-1.5 py-[2.5px] rounded bg-selection border border-strong flex items-center justify-center">
                      <span className="text-[10px]">⏎</span>
                    </div>
                    <p className="text-xs text-foreground-light">Save changes</p>
                  </div>
                  <div className="flex items-center space-x-2">
                    <div className="px-1 py-[2.5px] rounded bg-selection border border-strong flex items-center justify-center">
                      <span className="text-[10px]">Esc</span>
                    </div>
                    <p className="text-xs text-foreground-light">Cancel changes</p>
                  </div>
                </div>
              )}
              <Tooltip>
                <TooltipTrigger asChild>
                  <div
                    className={[
                      'border border-strong rounded p-1 flex items-center justify-center',
                      'transition cursor-pointer bg-selection hover:bg-border-strong',
                    ].join(' ')}
                    onClick={() => onSelectExpand()}
                  >
                    <Maximize size={12} strokeWidth={2} />
                  </div>
                </TooltipTrigger>
                <TooltipContent side="bottom" align="center">
                  <span>Expand editor</span>
                </TooltipContent>
              </Tooltip>
            </div>
          </BlockKeys>
        )
      }
    >
      <div
        className={`${
          !!value && jsonString.trim().length == 0 ? 'sb-grid-fill-container' : ''
        } sb-grid-json-editor__trigger`}
        onClick={() => setIsPopoverOpen(!isPopoverOpen)}
      >
        {value === null || value === '' ? <NullValue /> : jsonString}
      </div>
    </Popover>
  )
}

Subdomains

Frequently Asked Questions

What does JsonEditor() do?
JsonEditor() is a function in the supabase codebase.
What does JsonEditor() call?
JsonEditor() calls 2 function(s): tryFormatInitialValue, verifyJSON.

Analyze Your Own Codebase

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

Try Supermodel Free