How-to guides
Defining Data Types

Defining data types for any data

The defineDataType function allows you to define custom data types for any data structure. This is useful for:

  • Adding support for data types not supported by the library.
  • Customizing or extending the appearance and functionality of existing data types.

Introduction

To define a data type using defineDataType, you provide an object with various properties, such as is, Component, PreComponent, PostComponent, and Editor. These properties enable you to specify how to match values of the data type, how to render them, and how to edit them.

Before we dive into the details of defining a data type, let's take a closer look at the object that you'll need to provide to defineDataType.

is - The Type Matcher

The is function takes a value and a path and returns true if the value belongs to the data type being defined.

Component - The Renderer

The Component prop is a React component that renders the value of the data type. It receives a DataItemProps object as a prop, which includes the following:

  • props.path - The path to the value.
  • props.value - The value to render.
  • props.inspect - A Boolean flag indicating whether the value is being inspected (expanded).
  • props.setInspect - A function that can be used to toggle the inspect state.

PreComponent and PostComponent

For advanced use cases, you can provide PreComponent and PostComponent to render content before and after the value, respectively. This is useful if you want to add a button or implement an expandable view.

Editor

To enable editing for a data type, you need to provide serialize and deserialize functions to convert the value to and from a string representation. You can then use the Editor component to provide a custom editor for the stringified value. When the user edits the value, it will be parsed using deserialize, and the result will be passed to the onChange callback.

  • props.path - The path to the value.
  • props.value - The value to edit.
  • props.setValue - A function that can be used to update the value.
  • props.abortEditing - A function that can be used to abort editing.
  • props.commitEditing - A function that can be used to commit the value and finish editing.

Examples

Adding support for image

Suppose you have a JSON object that includes an image URL:

{
  "image": "https://i.imgur.com/1bX5QH6.jpg"
}

If you want to preview images directly in your JSON, you can define a data type for images:

import { defineDataType } from '@textea/json-viewer'
 
const imageType = defineDataType({
  is: (value) => {
    if (typeof value !== 'string') return false
    try {
      const url = new URL(value)
      return url.pathname.endsWith('.jpg')
    } catch {
      return false
    }
  },
  Component: (props) => {
    return (
      <img
        height={48}
        width={48}
        src={props.value}
        alt={props.value}
        style={{ display: 'inline-block' }}
      />
    )
  }
})

And then use it like this:

<JsonViewer
  value={{
    image: 'https://i.imgur.com/1bX5QH6.jpg'
  }}
  valueTypes={[imageType]}
/>

{
"image"
:
string
"https://i.imgur.com/1bX5QH6.jpg"
}

Adding support for URL

Let's add support for the URL (opens in a new tab) API.

import { defineDataType } from '@textea/json-viewer'
 
const urlType = defineDataType({
  is: (value) => value instanceof URL,
  Component: (props) => {
    const url = props.value.toString()
    return (
      <a
        href={url}
        target='_blank'
        rel='noopener noreferrer'
        style={{
          cursor: 'pointer',
          color: '#1976d2',
          textDecoration: 'underline'
        }}
      >
        {url}
      </a>
    )
  }
})

And then use it like this:

<JsonViewer
  value={{
    string: 'this is a string',
    url: new URL('https://example.com')
  }}
  valueTypes={[urlType]}
/>

It should looks like this 🎉


{
"string"
:
string
"this is a string"
"url"
:
{
}
}

Adding widgets to the value

Sometimes you might want to add a button to the value.

{
"link"
:
string
"http://example.com"
}

In this example, we will extend the built-in stringType and add a button to open the link in a new tab.

import { defineDataType, JsonViewer, stringType } from '@textea/json-viewer'
import { Button } from '@mui/material'
 
const linkType = defineDataType({
  ...stringType,
  is (value) {
    return typeof value === 'string' && value.startsWith('http')
  },
  PostComponent: (props) => (
    <Button
      variant='contained'
      size='small'
      href={props.value}
      target='_blank'
      rel='noopener noreferrer'
      sx={{
        display: 'inline-block',
        marginLeft: 1
      }}
    >
      Open
      <LinkIcon sx={{ strokeWidth: 2 }} />
    </Button>
  )
})

Customize the Date format

By default, Date values are displayed using the toLocaleString method. You will learn how to create a custom data type for Date and display it in a different format. We will use the enhanced defineEasyType function to simplify the process.

import { defineEasyType } from '@textea/json-viewer'
 
const myDateType = defineEasyType({
  is: (value) => value instanceof Date,
  type: 'date',
  colorKey: 'base0D',
  Renderer: ({ value }) => <>{value.toISOString().split('T')[0]}</>
})
{
"date"
:
date
Wed, Apr 12, 2023, 12:34 PM
}