| <script lang="ts"> | |
| import { onDestroy } from "svelte"; | |
| import { JSON as JSONIcon } from "@gradio/icons"; | |
| import { Empty, IconButtonWrapper, IconButton } from "@gradio/atoms"; | |
| import JSONNode from "./JSONNode.svelte"; | |
| import { Copy, Check } from "@gradio/icons"; | |
| export let value: any = {}; | |
| export let open = false; | |
| export let theme_mode: "system" | "light" | "dark" = "system"; | |
| export let show_indices = false; | |
| export let label_height: number; | |
| $: json_max_height = `calc(100% - ${label_height}px)`; | |
| let copied = false; | |
| let timer: NodeJS.Timeout; | |
| function copy_feedback(): void { | |
| copied = true; | |
| if (timer) clearTimeout(timer); | |
| timer = setTimeout(() => { | |
| copied = false; | |
| }, 1000); | |
| } | |
| async function handle_copy(): Promise<void> { | |
| if ("clipboard" in navigator) { | |
| await navigator.clipboard.writeText(JSON.stringify(value, null, 2)); | |
| copy_feedback(); | |
| } | |
| } | |
| function is_empty(obj: object): boolean { | |
| return ( | |
| obj && | |
| Object.keys(obj).length === 0 && | |
| Object.getPrototypeOf(obj) === Object.prototype && | |
| JSON.stringify(obj) === JSON.stringify({}) | |
| ); | |
| } | |
| onDestroy(() => { | |
| if (timer) clearTimeout(timer); | |
| }); | |
| </script> | |
| {#if value && value !== '""' && !is_empty(value)} | |
| <IconButtonWrapper> | |
| <IconButton | |
| show_label={false} | |
| label={copied ? "Copied" : "Copy"} | |
| Icon={copied ? Check : Copy} | |
| on:click={() => handle_copy()} | |
| /> | |
| </IconButtonWrapper> | |
| <div class="json-holder" style:max-height={json_max_height}> | |
| <JSONNode | |
| {value} | |
| depth={0} | |
| is_root={true} | |
| {open} | |
| {theme_mode} | |
| {show_indices} | |
| /> | |
| </div> | |
| {:else} | |
| <div class="empty-wrapper"> | |
| <Empty> | |
| <JSONIcon /> | |
| </Empty> | |
| </div> | |
| {/if} | |
| <style> | |
| :global(.copied svg) { | |
| animation: fade ease 300ms; | |
| animation-fill-mode: forwards; | |
| } | |
| @keyframes fade { | |
| 0% { | |
| opacity: 0; | |
| } | |
| 100% { | |
| opacity: 1; | |
| } | |
| } | |
| .json-holder { | |
| padding: var(--size-2); | |
| overflow-y: auto; | |
| } | |
| .empty-wrapper { | |
| min-height: calc(var(--size-32) - 20px); | |
| height: 100%; | |
| } | |
| </style> | |