iT邦幫忙

2024 iThome 鐵人賽

DAY 28
0

假如只是需要展示一個 JSON Object ,那其實不需要特別設計,只需要

  1. json.Marshal
  2. 前端使用 Code Component

但我們現在需要它可以摺疊,所以需要特別為寫 JSON 寫樹狀遞迴 Component 。

TJson

這是 Component 的入口,他會把從後端傳過來的 JSON string parse 好後餵給 JsonValue

export function TJson({ node }: Props) {
  return (
    <div className="block">
      <pre>
        <JsonValue val={JSON.parse(node.props.value)} pad={0} />
      </pre>
    </div>
  )
}

JsonValue

JsonValue 是一個 Factory ,他會根據傳進來的 Type 決定他要丟給以下哪個 Componet 處理:

function JsonValue({ val, pad }: { val: any, pad: number }) {
  if (val === null) {
    return JsonNull({ val })
  }

  if (Array.isArray(val)) {
    return JsonList({ val, pad })
  } else if (typeof val === 'object') {
    return JsonDict({ val, pad })
  } else if (typeof val === 'string') {
    return JsonString({ val })
  } else if (typeof val === 'boolean') {
    return JsonBool({ val })
  } else {
    return JsonElement({ val })
  }
}

JsonNull , JsonString, JsonElement , JsonBool

對於字串、布林值、null 以及一般的基本型別,使用專屬的渲染方法。例如,字串會被顯示為綠色,而布林值和 null 則以棕色顯示,這有助於快速識別 JSON 的資料型態。

function JsonString({ val }: { val: string }) {
  return <p style={{ display: 'inline', color: 'green' }} >"{val}"</p>
}

function JsonBool({ val }: { val: boolean }) {
  if (val) {
    return <p style={{ display: 'inline', color: 'brown' }} >true</p>
  } else {
    return <p style={{ display: 'inline', color: 'brown' }} >false</p>
  }
}

function JsonNull({ val }: { val: null }) {
  return <p style={{ display: 'inline', color: 'brown' }} >null</p>
}

function JsonElement({ val }: { val: any }) {
  return <p style={{ display: 'inline', color: 'brown' }}>{val}</p>
}

JsonDict

這兩個是這 TJson 的關鍵,要可以摺疊

我們使用 useState 來控制物件的展開與折疊狀態。當物件被展開時,它會遞迴地渲染每個 key-value 對,並且在每一層加上適當的縮排。

function JsonDict({ val, pad }: { val: any, pad: number }) {
  const [open, setOpen] = useState(true)
  if (!open) {
    return <p style={{ display: 'inline' }}>
      <span className="pseudolink" onClick={(e) => { setOpen(true) }}>
        <b>{"{ ... }"}</b>
      </span>
    </p>
  }

  const kvs = []
  for (const [key, value] of Object.entries(val)) {
    kvs.push(<div>{' '.repeat(pad + 2)}"{key}": <JsonValue val={value} pad={pad + 2} />,</div>)
  }
  return (
    <p style={{ display: 'inline' }}>
      <span className="pseudolink" onClick={(e) => { setOpen(false) }}>
        <b>{"{"}</b>
      </span>
      <div>{kvs}</div>
      <span className="pseudolink" onClick={(e) => { setOpen(false) }}>
        <b>{' '.repeat(pad)}{"}"}</b>
      </span>
    </p>
  )
}

JsonList

類似 JsonDict

function JsonList({ val, pad }: { val: { [key: string]: any }, pad: number }) {
  const [open, setOpen] = useState(true)
  if (!open) {
    return <p style={{ display: 'inline' }}>
      <span className="pseudolink" onClick={(e) => { setOpen(true) }}>
        <b>{"[ ... ]"}</b>
      </span>
    </p>
  }

  return (
    <p style={{ display: 'inline' }}>
      <span className="pseudolink" onClick={(e) => { setOpen(false) }}>
        <b>{"["}</b>
      </span>
      <div>{
        val.map((value: any) => (
          <div>{' '.repeat(pad + 2)}<JsonValue val={value} pad={pad + 2} />,</div>
        ))
      }</div>
      <span className="pseudolink" onClick={(e) => { setOpen(false) }}>
        <b>{' '.repeat(pad)}{"]"}</b>
      </span>
    </p>
  )
}

效果 (Demo)


上一篇
Day27 Local App: Webview
下一篇
Day29 Echo
系列文
用 Golang 實作 streamlit 30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言