|
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 = $state() as HTMLLIElement; |
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