|
9 | 9 | interface Props { |
10 | 10 | key?: string; |
11 | 11 | value: Ast; |
12 | | - root?: boolean; |
13 | 12 | path_nodes?: Ast[]; |
14 | 13 | autoscroll?: boolean; |
| 14 | + depth?: number; |
15 | 15 | } |
16 | 16 |
|
17 | | - let { key = '', value, root = false, path_nodes = [], autoscroll = true }: Props = $props(); |
| 17 | + let { key = '', value, path_nodes = [], autoscroll = true, depth = 0 }: Props = $props(); |
18 | 18 |
|
19 | 19 | const { toggleable } = get_repl_context(); |
20 | 20 |
|
21 | | - let collapsed = $state(!root); |
| 21 | + let root = depth === 0; |
| 22 | + let open = $state(root); |
22 | 23 |
|
23 | 24 | let list_item_el = $state() as HTMLLIElement; |
24 | 25 |
|
|
34 | 35 | ); |
35 | 36 | let key_text = $derived(key ? `${key}:` : ''); |
36 | 37 |
|
37 | | - let preview_text = $state(''); |
38 | | -
|
39 | 38 | $effect(() => { |
40 | | - if (is_primitive || !collapsed) return; |
41 | | -
|
42 | | - if (is_array) { |
43 | | - if (!('length' in value)) return; |
44 | | -
|
45 | | - preview_text = `[ ${value.length} element${value.length === 1 ? '' : 's'} ]`; |
46 | | - } else { |
47 | | - preview_text = `{ ${Object.keys(value).join(', ')} }`; |
48 | | - } |
49 | | - }); |
50 | | -
|
51 | | - $effect(() => { |
52 | | - collapsed = !path_nodes.includes(value); |
| 39 | + open = path_nodes.includes(value); |
53 | 40 | }); |
54 | 41 |
|
55 | 42 | $effect(() => { |
|
95 | 82 | onfocus={handle_mark_text} |
96 | 83 | onmouseleave={handle_unmark_text} |
97 | 84 | > |
98 | | - {#if !root && !is_primitive} |
99 | | - <button class="ast-toggle" class:open={!collapsed} onclick={() => (collapsed = !collapsed)}> |
100 | | - {key_text} |
101 | | - </button> |
102 | | - {:else if key_text} |
103 | | - <span>{key_text}</span> |
104 | | - {/if} |
105 | | - |
106 | | - {#if is_primitive} |
107 | | - <span class="token {typeof value}"> |
108 | | - {JSON.stringify(value)} |
| 85 | + {#if is_primitive || (is_array && value.length === 0)} |
| 86 | + <span class="value"> |
| 87 | + {#if key_text} |
| 88 | + <span>{key_text}</span> |
| 89 | + {/if} |
| 90 | + |
| 91 | + {#if value == undefined} |
| 92 | + <span class="token comment">{String(value)}</span> |
| 93 | + {:else} |
| 94 | + <span class="token {typeof value}"> |
| 95 | + {JSON.stringify(value)} |
| 96 | + </span> |
| 97 | + {/if} |
109 | 98 | </span> |
110 | | - {:else if collapsed && !root} |
111 | | - <button class="preview" onclick={() => (collapsed = !collapsed)}> |
112 | | - {preview_text} |
113 | | - </button> |
114 | 99 | {:else} |
115 | | - <span>{is_array ? '[' : '{'}</span> |
116 | | - <ul> |
117 | | - {#each Object.entries(value) as [k, v]} |
118 | | - <AstNode key={is_array ? '' : k} value={v} {path_nodes} {autoscroll} /> |
119 | | - {/each} |
120 | | - </ul> |
121 | | - <span>{is_array ? ']' : '}'}</span> |
| 100 | + <details bind:open> |
| 101 | + <summary> |
| 102 | + {#if key} |
| 103 | + <span class="key">{key}</span>: |
| 104 | + {/if} |
| 105 | + |
| 106 | + {#if is_array} |
| 107 | + [{#if !open} |
| 108 | + <span class="token comment">...</span>] |
| 109 | + <span class="token comment">({value.length})</span> |
| 110 | + {/if} |
| 111 | + {:else} |
| 112 | + {#if value.type} |
| 113 | + <span class="token comment">{value.type}</span> |
| 114 | + {/if} |
| 115 | + {'{'}{#if !open}<span class="token comment">...</span>}{/if} |
| 116 | + {/if} |
| 117 | + </summary> |
| 118 | + |
| 119 | + {#if is_array} |
| 120 | + <ul> |
| 121 | + {#each value as v} |
| 122 | + <AstNode value={v} {path_nodes} {autoscroll} depth={depth + 1} /> |
| 123 | + {/each} |
| 124 | + </ul> |
| 125 | + <span>]</span> |
| 126 | + {:else} |
| 127 | + <ul> |
| 128 | + {#each Object.entries(value) as [k, v]} |
| 129 | + <AstNode key={k} value={v} {path_nodes} {autoscroll} depth={depth + 1} /> |
| 130 | + {/each} |
| 131 | + </ul> |
| 132 | + <span>}</span> |
| 133 | + {/if} |
| 134 | + </details> |
122 | 135 | {/if} |
123 | 136 | </li> |
124 | 137 |
|
|
137 | 150 | background-color: var(--sk-highlight-color); |
138 | 151 | } |
139 | 152 |
|
140 | | - button { |
141 | | - &:hover { |
142 | | - text-decoration: underline; |
143 | | - } |
144 | | - } |
145 | | -
|
146 | | - .ast-toggle { |
| 153 | + summary { |
147 | 154 | position: relative; |
148 | | - } |
| 155 | + display: block; |
| 156 | + cursor: pointer; |
149 | 157 |
|
150 | | - .ast-toggle::before { |
151 | | - content: '\25B6'; |
152 | | - position: absolute; |
153 | | - bottom: 0; |
154 | | - left: -1.3rem; |
155 | | - opacity: 0.7; |
156 | | - } |
| 158 | + .key { |
| 159 | + text-decoration: underline; |
| 160 | + } |
157 | 161 |
|
158 | | - .ast-toggle.open::before { |
159 | | - content: '\25BC'; |
| 162 | + &:hover .key { |
| 163 | + text-decoration: underline; |
| 164 | + } |
160 | 165 | } |
161 | 166 |
|
162 | 167 | .token { |
|
170 | 175 | .token.number { |
171 | 176 | color: var(--sk-code-number); |
172 | 177 | } |
| 178 | +
|
| 179 | + .token.comment { |
| 180 | + color: var(--sk-code-comment); |
| 181 | + } |
173 | 182 | </style> |
0 commit comments