|
1 | 1 | <script lang="ts">
|
| 2 | + import AstNode from './AstNode.svelte'; |
2 | 3 | import { get_repl_context } from '../context';
|
3 | 4 | import { tick } from 'svelte';
|
4 | 5 | import type { CompileResult } from 'svelte/compiler';
|
5 | 6 |
|
6 | 7 | type Ast = CompileResult['ast'];
|
7 | 8 |
|
8 |
| - export let key = ''; |
9 |
| - export let value: Ast; |
10 |
| - export let collapsed = true; |
11 |
| - export let path_nodes: Ast[] = []; |
12 |
| - export let autoscroll = true; |
| 9 | + interface Props { |
| 10 | + key?: string; |
| 11 | + value: Ast; |
| 12 | + collapsed?: boolean; |
| 13 | + path_nodes?: Ast[]; |
| 14 | + autoscroll?: boolean; |
| 15 | + } |
| 16 | +
|
| 17 | + let { |
| 18 | + key = '', |
| 19 | + value, |
| 20 | + collapsed = $bindable(true), |
| 21 | + path_nodes = [], |
| 22 | + autoscroll = true |
| 23 | + }: Props = $props(); |
13 | 24 |
|
14 | 25 | const { toggleable } = get_repl_context();
|
15 | 26 |
|
16 |
| - let list_item_el: HTMLLIElement; |
| 27 | + let list_item_el: HTMLLIElement = $state(); |
17 | 28 |
|
18 |
| - $: is_root = path_nodes[0] === value; |
19 |
| - $: is_leaf = path_nodes[path_nodes.length - 1] === value; |
20 |
| - $: is_ast_array = Array.isArray(value); |
21 |
| - $: is_collapsable = value && typeof value === 'object'; |
22 |
| - $: is_markable = |
| 29 | + let is_root = $derived(path_nodes[0] === value); |
| 30 | + let is_leaf = $derived(path_nodes[path_nodes.length - 1] === value); |
| 31 | + let is_ast_array = $derived(Array.isArray(value)); |
| 32 | + let is_collapsable = $derived(value && typeof value === 'object'); |
| 33 | + let is_markable = $derived( |
23 | 34 | is_collapsable &&
|
24 |
| - 'start' in value && |
25 |
| - 'end' in value && |
26 |
| - typeof value.start === 'number' && |
27 |
| - typeof value.end === 'number'; |
28 |
| - $: key_text = key ? `${key}:` : ''; |
| 35 | + 'start' in value && |
| 36 | + 'end' in value && |
| 37 | + typeof value.start === 'number' && |
| 38 | + typeof value.end === 'number' |
| 39 | + ); |
| 40 | + let key_text = $derived(key ? `${key}:` : ''); |
| 41 | +
|
| 42 | + let preview_text = $state(''); |
29 | 43 |
|
30 |
| - let preview_text = ''; |
31 |
| - $: { |
32 |
| - if (!is_collapsable || !collapsed) break $; |
| 44 | + $effect(() => { |
| 45 | + if (!is_collapsable || !collapsed) return; |
33 | 46 |
|
34 | 47 | if (is_ast_array) {
|
35 |
| - if (!('length' in value)) break $; |
| 48 | + if (!('length' in value)) return; |
36 | 49 |
|
37 | 50 | preview_text = `[ ${value.length} element${value.length === 1 ? '' : 's'} ]`;
|
38 | 51 | } else {
|
39 | 52 | preview_text = `{ ${Object.keys(value).join(', ')} }`;
|
40 | 53 | }
|
41 |
| - } |
42 |
| -
|
43 |
| - $: collapsed = !path_nodes.includes(value); |
44 |
| -
|
45 |
| - $: if (autoscroll && is_leaf && !$toggleable) { |
46 |
| - // wait for all nodes to render before scroll |
47 |
| - tick().then(() => { |
48 |
| - if (list_item_el) { |
49 |
| - list_item_el.scrollIntoView(); |
50 |
| - } |
51 |
| - }); |
52 |
| - } |
| 54 | + }); |
| 55 | +
|
| 56 | + $effect(() => { |
| 57 | + collapsed = !path_nodes.includes(value); |
| 58 | + }); |
| 59 | +
|
| 60 | + $effect(() => { |
| 61 | + if (autoscroll && is_leaf && !$toggleable) { |
| 62 | + // wait for all nodes to render before scroll |
| 63 | + tick().then(() => { |
| 64 | + if (list_item_el) { |
| 65 | + list_item_el.scrollIntoView(); |
| 66 | + } |
| 67 | + }); |
| 68 | + } |
| 69 | + }); |
53 | 70 |
|
54 | 71 | function handle_mark_text(e: MouseEvent | FocusEvent) {
|
55 | 72 | if (is_markable) {
|
|
79 | 96 | <li
|
80 | 97 | bind:this={list_item_el}
|
81 | 98 | class:marked={!is_root && is_leaf}
|
82 |
| - on:mouseover={handle_mark_text} |
83 |
| - on:focus={handle_mark_text} |
84 |
| - on:mouseleave={handle_unmark_text} |
| 99 | + onmouseover={handle_mark_text} |
| 100 | + onfocus={handle_mark_text} |
| 101 | + onmouseleave={handle_unmark_text} |
85 | 102 | >
|
86 | 103 | {#if !is_root && is_collapsable}
|
87 |
| - <button class="ast-toggle" class:open={!collapsed} on:click={() => (collapsed = !collapsed)}> |
| 104 | + <button class="ast-toggle" class:open={!collapsed} onclick={() => (collapsed = !collapsed)}> |
88 | 105 | {key_text}
|
89 | 106 | </button>
|
90 | 107 | {:else if key_text}
|
91 | 108 | <span>{key_text}</span>
|
92 | 109 | {/if}
|
93 | 110 | {#if is_collapsable}
|
94 | 111 | {#if collapsed && !is_root}
|
95 |
| - <button class="preview" on:click={() => (collapsed = !collapsed)}> |
| 112 | + <button class="preview" onclick={() => (collapsed = !collapsed)}> |
96 | 113 | {preview_text}
|
97 | 114 | </button>
|
98 | 115 | {:else}
|
99 | 116 | <span>{is_ast_array ? '[' : '{'}</span>
|
100 | 117 | <ul>
|
101 | 118 | {#each Object.entries(value) as [k, v]}
|
102 |
| - <svelte:self key={is_ast_array ? '' : k} value={v} {path_nodes} {autoscroll} /> |
| 119 | + <AstNode key={is_ast_array ? '' : k} value={v} {path_nodes} {autoscroll} /> |
103 | 120 | {/each}
|
104 | 121 | </ul>
|
105 | 122 | <span>{is_ast_array ? ']' : '}'}</span>
|
|
0 commit comments