CoolFace
Datasetpublic

gradio/frontend

sourceHugging Faceupdated 15h agoView on Hugging Face
1likes442kdownloads
JSONNode.svelte285 linesDownload Raw Back to shared
1<script lang="ts">2	import { tick } from "svelte";3	import Self from "./JSONNode.svelte";4 5	let {6		value,7		depth = 0,8		is_root = false,9		is_last_item = true,10		key = null,11		open = false,12		theme_mode = "system" as "system" | "light" | "dark",13		show_indices = false,14		interactive = true15	}: {16		value: any;17		depth?: number;18		is_root?: boolean;19		is_last_item?: boolean;20		key?: string | number | null;21		open?: boolean;22		theme_mode?: "system" | "light" | "dark";23		show_indices?: boolean;24		interactive?: boolean;25	} = $props();26 27	let root_element = $state<HTMLElement>();28	let collapsed = $state(open ? false : depth >= 3);29	let child_nodes = $state<any[]>([]);30 31	function is_collapsible(val: any): boolean {32		return val !== null && (typeof val === "object" || Array.isArray(val));33	}34 35	async function toggle_collapse(): Promise<void> {36		collapsed = !collapsed;37		await tick();38	}39 40	function get_collapsed_preview(val: any): string {41		if (Array.isArray(val)) return `Array(${val.length})`;42		if (typeof val === "object" && val !== null)43			return `Object(${Object.keys(val).length})`;44		return String(val);45	}46 47	$effect(() => {48		if (is_collapsible(value)) {49			child_nodes = Object.entries(value);50		} else {51			child_nodes = [];52		}53	});54 55	function updateLineNumbers(): void {56		if (!root_element) return;57		const lines = root_element.querySelectorAll(".line");58		lines.forEach((line, index) => {59			const line_number = line.querySelector(".line-number");60			if (line_number) {61				line_number.setAttribute("data-pseudo-content", (index + 1).toString());62				line_number?.setAttribute(63					"aria-roledescription",64					`Line number ${index + 1}`65				);66				line_number?.setAttribute("title", `Line number ${index + 1}`);67			}68		});69	}70 71	$effect(() => {72		value;73		collapsed;74 75		if (is_root && root_element) {76			tick().then(() => {77				updateLineNumbers();78			});79		}80	});81</script>82 83<div84	class="json-node"85	class:root={is_root}86	class:dark-mode={theme_mode === "dark"}87	bind:this={root_element}88	style="--depth: {depth};"89>90	<div class="line" class:collapsed>91		<span class="line-number"></span>92		<span class="content">93			{#if is_collapsible(value)}94				<button95					data-pseudo-content={interactive ? (collapsed ? "▶" : "▼") : ""}96					aria-label={collapsed ? "Expand" : "Collapse"}97					class="toggle"98					disabled={!interactive}99					onclick={toggle_collapse}100				/>101			{/if}102			{#if key !== null}103				<span class="key">"{key}"</span><span class="punctuation colon"104					>:105				</span>106			{/if}107			{#if is_collapsible(value)}108				<span109					class="punctuation bracket"110					class:square-bracket={Array.isArray(value)}111					>{Array.isArray(value) ? "[" : "{"}</span112				>113				{#if collapsed}114					<button onclick={toggle_collapse} class="preview">115						{get_collapsed_preview(value)}116					</button>117					<span118						class="punctuation bracket"119						class:square-bracket={Array.isArray(value)}120						>{Array.isArray(value) ? "]" : "}"}</span121					>122				{/if}123			{:else if typeof value === "string"}124				<span class="value string">"{value}"</span>125			{:else if typeof value === "number"}126				<span class="value number">{value}</span>127			{:else if typeof value === "boolean"}128				<span class="value bool">{value.toString()}</span>129			{:else if value === null}130				<span class="value null">null</span>131			{:else}132				<span>{value}</span>133			{/if}134			{#if !is_last_item && (!is_collapsible(value) || collapsed)}135				<span class="punctuation">,</span>136			{/if}137		</span>138	</div>139 140	{#if is_collapsible(value)}141		<div class="children" class:hidden={collapsed}>142			{#each child_nodes as [subKey, subVal], i}143				<Self144					value={subVal}145					depth={depth + 1}146					is_last_item={i === child_nodes.length - 1}147					key={Array.isArray(value) && !show_indices ? null : subKey}148					{open}149					{theme_mode}150					{show_indices}151					{interactive}152				/>153			{/each}154			<div class="line">155				<span class="line-number"></span>156				<span class="content">157					<span158						class="punctuation bracket"159						class:square-bracket={Array.isArray(value)}160						>{Array.isArray(value) ? "]" : "}"}</span161					>162					{#if !is_last_item}<span class="punctuation">,</span>{/if}163				</span>164			</div>165		</div>166	{/if}167</div>168 169<style>170	.json-node {171		font-family: var(--font-mono);172		--text-color: #d18770;173		--key-color: var(--text-color);174		--string-color: #ce9178;175		--number-color: #719fad;176 177		--bracket-color: #5d8585;178		--square-bracket-color: #be6069;179		--punctuation-color: #8fbcbb;180		--line-number-color: #6a737d;181		--separator-color: var(--line-number-color);182	}183	.json-node.dark-mode {184		--bracket-color: #7eb4b3;185		--number-color: #638d9a;186	}187	.json-node.root {188		position: relative;189		padding-left: var(--size-14);190	}191	.json-node.root::before {192		content: "";193		position: absolute;194		top: 0;195		bottom: 0;196		left: var(--size-11);197		width: 1px;198		background-color: var(--separator-color);199	}200	.line {201		display: flex;202		align-items: flex-start;203		padding: 0;204		margin: 0;205		line-height: var(--line-md);206	}207	.line-number {208		position: absolute;209		left: 0;210		width: calc(var(--size-7));211		text-align: right;212		color: var(--line-number-color);213		user-select: none;214		text-overflow: ellipsis;215		text-overflow: ellipsis;216		direction: rtl;217		overflow: hidden;218	}219	.content {220		flex: 1;221		display: flex;222		align-items: center;223		padding-left: calc(var(--depth) * var(--size-2));224		flex-wrap: wrap;225	}226	.children {227		padding-left: var(--size-4);228	}229	.children.hidden {230		display: none;231	}232	.key {233		color: var(--key-color);234	}235	.string {236		color: var(--string-color);237	}238	.number {239		color: var(--number-color);240	}241	.bool {242		color: var(--text-color);243	}244	.null {245		color: var(--text-color);246	}247	.value {248		margin-left: var(--spacing-md);249	}250	.punctuation {251		color: var(--punctuation-color);252	}253	.bracket {254		margin-left: var(--spacing-sm);255		color: var(--bracket-color);256	}257	.square-bracket {258		margin-left: var(--spacing-sm);259		color: var(--square-bracket-color);260	}261	.toggle,262	.preview {263		background: none;264		border: none;265		color: inherit;266		cursor: pointer;267		padding: 0;268		margin: 0;269	}270	.toggle {271		user-select: none;272		margin-right: var(--spacing-md);273	}274	.preview {275		margin: 0 var(--spacing-sm) 0 var(--spacing-lg);276	}277	.preview:hover {278		text-decoration: underline;279	}280 281	:global([data-pseudo-content])::before {282		content: attr(data-pseudo-content);283	}284</style>285 
gradio/frontend · CoolFace