|
7 | 7 | export let show_modified;
|
8 | 8 |
|
9 | 9 | /** @type {ReturnType<typeof createEventDispatcher<{
|
10 |
| - * remove: { files: import('$lib/types').File[] }, |
11 |
| - * add: { files: import('$lib/types').File[] }, |
| 10 | + * remove: { files: import('$lib/types').File[]; diff: import('$lib/types').File }, |
| 11 | + * add: { files: import('$lib/types').File[]; diff: import('$lib/types').File }, |
12 | 12 | * }>>} */
|
13 | 13 | const dispatch = createEventDispatcher();
|
14 | 14 |
|
|
18 | 18 | module_editor,
|
19 | 19 | rebundle,
|
20 | 20 | selected,
|
21 |
| - selected_index, |
| 21 | + selected_name, |
22 | 22 | EDITOR_STATE_MAP
|
23 | 23 | } = get_repl_context();
|
24 | 24 |
|
25 |
| - /** @type {number} */ |
26 |
| - let editing_index = -1; |
| 25 | + /** @type {string | null} */ |
| 26 | + let editing_name = null; |
| 27 | +
|
| 28 | + let input_value = ''; |
27 | 29 |
|
28 |
| - /** @param {number} index */ |
29 |
| - function select_file(index) { |
30 |
| - if ($selected_index !== index) { |
31 |
| - editing_index = -1; |
32 |
| - handle_select(index); |
| 30 | + /** @param {string} filename */ |
| 31 | + function select_file(filename) { |
| 32 | + if ($selected_name !== filename) { |
| 33 | + editing_name = null; |
| 34 | + handle_select(filename); |
33 | 35 | }
|
34 | 36 | }
|
35 | 37 |
|
36 |
| - /** @param {number} index */ |
37 |
| - function edit_tab(index) { |
38 |
| - if ($selected_index === index) { |
39 |
| - editing_index = $selected_index; |
| 38 | + /** @param {import('$lib/types').File} file */ |
| 39 | + function edit_tab(file) { |
| 40 | + if ($selected_name === get_full_filename(file)) { |
| 41 | + editing_name = get_full_filename(file); |
| 42 | + input_value = file.name; |
40 | 43 | }
|
41 | 44 | }
|
42 | 45 |
|
43 | 46 | async function close_edit() {
|
44 |
| - const match = /(.+)\.(svelte|js|json|md|css)$/.exec($selected?.name ?? ''); |
| 47 | + const match = /(.+)\.(svelte|js|json|md|css)$/.exec(input_value ?? ''); |
| 48 | +
|
| 49 | + const edited_file = $files.find((val) => get_full_filename(val) === editing_name); |
45 | 50 |
|
46 |
| - const edited_file = $files[editing_index]; |
47 |
| - edited_file.name = match ? match[1] : edited_file.name; |
| 51 | + if (!edited_file) return; |
| 52 | +
|
| 53 | + edited_file.name = match ? match[1] : input_value; |
48 | 54 |
|
49 | 55 | if (!$selected) return;
|
50 | 56 |
|
|
53 | 59 | let name = $selected.name;
|
54 | 60 |
|
55 | 61 | do {
|
56 |
| - $files[$selected_index].name = `${name}_${i++}`; |
| 62 | + const file = $files.find( |
| 63 | + (val) => |
| 64 | + get_full_filename(val) === get_full_filename(edited_file) && |
| 65 | + // @ts-ignore |
| 66 | + val.source === $selected.source |
| 67 | + ); |
| 68 | +
|
| 69 | + if (!file) break; |
| 70 | +
|
| 71 | + file.name = `${name}_${i++}`; |
57 | 72 | } while (is_file_name_used($selected));
|
58 | 73 |
|
59 |
| - $files[$selected_index] = edited_file; |
| 74 | + const idx = $files.findIndex( |
| 75 | + (val) => get_full_filename(val) === get_full_filename(edited_file) |
| 76 | + ); |
| 77 | + $files[idx] = edited_file; |
60 | 78 | }
|
61 | 79 |
|
62 |
| - if (match?.[2]) $files[$selected_index].type = match[2]; |
63 |
| -
|
64 |
| - editing_index = -1; |
| 80 | + const idx = $files.findIndex( |
| 81 | + (val) => get_full_filename(val) === get_full_filename(edited_file) |
| 82 | + ); |
| 83 | + if (match?.[2]) $files[idx].type = match[2]; |
| 84 | +
|
| 85 | + if (editing_name) { |
| 86 | + const old_state = EDITOR_STATE_MAP.get(editing_name); |
| 87 | + if (old_state) { |
| 88 | + EDITOR_STATE_MAP.set(get_full_filename(edited_file), old_state); |
| 89 | + EDITOR_STATE_MAP.delete(editing_name); |
| 90 | + } |
| 91 | + } |
65 | 92 |
|
66 |
| - EDITOR_STATE_MAP.delete(get_full_filename($selected)); |
| 93 | + editing_name = null; |
67 | 94 |
|
68 | 95 | // re-select, in case the type changed
|
69 |
| - handle_select($selected_index); |
| 96 | + handle_select(get_full_filename(edited_file)); |
| 97 | +
|
| 98 | + $files = $files; |
70 | 99 |
|
71 | 100 | // focus the editor, but wait a beat (so key events aren't misdirected)
|
72 | 101 | await tick();
|
|
77 | 106 | }
|
78 | 107 |
|
79 | 108 | /**
|
80 |
| - * @param {number} index |
| 109 | + * @param {string} filename |
81 | 110 | */
|
82 |
| - function remove(index) { |
83 |
| - let result = confirm( |
84 |
| - `Are you sure you want to delete ${$files[index].name}.${$files[index].type}?` |
85 |
| - ); |
| 111 | + function remove(filename) { |
| 112 | + const file = $files.find((val) => get_full_filename(val) === filename); |
| 113 | + const idx = $files.findIndex((val) => get_full_filename(val) === filename); |
| 114 | +
|
| 115 | + if (!file) return; |
| 116 | +
|
| 117 | + let result = confirm(`Are you sure you want to delete ${get_full_filename(file)}?`); |
86 | 118 |
|
87 | 119 | if (!result) return;
|
88 | 120 |
|
89 |
| - if (index !== -1) { |
90 |
| - $files = $files.filter((_, idx) => idx !== index); |
| 121 | + $files = $files.filter((file) => get_full_filename(file) !== filename); |
91 | 122 |
|
92 |
| - dispatch('remove', { files: $files }); |
93 |
| - } else { |
94 |
| - console.error(`Could not find component! That's... odd`); |
95 |
| - } |
| 123 | + dispatch('remove', { files: $files, diff: file }); |
96 | 124 |
|
97 |
| - EDITOR_STATE_MAP.delete(get_full_filename($files[index])); |
98 |
| - handle_select(($selected_index = index - 1)); |
| 125 | + EDITOR_STATE_MAP.delete(get_full_filename(file)); |
| 126 | + handle_select(($selected_name = idx === 1 ? 'App.svelte' : get_full_filename(file))); |
99 | 127 | }
|
100 | 128 |
|
101 | 129 | /** @param {FocusEvent & { currentTarget: HTMLInputElement }} event */
|
|
117 | 145 |
|
118 | 146 | $files = $files.concat(file);
|
119 | 147 |
|
120 |
| - editing_index = $files.length - 1; |
| 148 | + editing_name = get_full_filename(file); |
| 149 | +
|
| 150 | + input_value = file.name; |
| 151 | +
|
| 152 | + handle_select(editing_name); |
121 | 153 |
|
122 |
| - handle_select(editing_index); |
| 154 | + rebundle(); |
| 155 | +
|
| 156 | + dispatch('add', { files: $files, diff: file }); |
123 | 157 |
|
124 |
| - dispatch('add', { files: $files }); |
| 158 | + $files = $files; |
125 | 159 | }
|
126 | 160 |
|
127 | 161 | /** @param {import('$lib/types').File} editing */
|
|
171 | 205 | <div class="component-selector">
|
172 | 206 | {#if $files.length}
|
173 | 207 | <div class="file-tabs" on:dblclick={add_new}>
|
174 |
| - {#each $files as file, index (file)} |
| 208 | + {#each $files as file, index (file.name)} |
| 209 | + {@const filename = get_full_filename(file)} |
175 | 210 | <div
|
176 | 211 | id={file.name}
|
177 | 212 | class="button"
|
178 | 213 | role="button"
|
179 | 214 | tabindex="0"
|
180 |
| - class:active={index === $selected_index} |
181 |
| - class:draggable={index !== editing_index && index !== 0} |
| 215 | + class:active={filename === $selected_name} |
| 216 | + class:draggable={filename !== editing_name && index !== 0} |
182 | 217 | class:drag-over={over === file.name}
|
183 |
| - on:click={() => select_file(index)} |
184 |
| - on:keyup={(e) => e.key === ' ' && select_file(index)} |
| 218 | + on:click={() => select_file(filename)} |
| 219 | + on:keyup={(e) => e.key === ' ' && select_file(filename)} |
185 | 220 | on:dblclick|stopPropagation={() => {}}
|
186 |
| - draggable={index !== editing_index} |
| 221 | + draggable={filename !== editing_name} |
187 | 222 | on:dragstart={dragStart}
|
188 | 223 | on:dragover|preventDefault={dragOver}
|
189 | 224 | on:dragleave={dragLeave}
|
190 | 225 | on:drop|preventDefault={dragEnd}
|
191 | 226 | >
|
192 | 227 | <i class="drag-handle" />
|
193 |
| - {#if file.name === 'App' && index !== editing_index} |
| 228 | + {#if file.name === 'App' && filename !== editing_name} |
194 | 229 | <div class="uneditable">
|
195 | 230 | App.svelte{#if show_modified && file.modified}*{/if}
|
196 | 231 | </div>
|
197 |
| - {:else if index === editing_index} |
198 |
| - {@const file = $files[editing_index]} |
199 |
| -
|
200 |
| - <span class="input-sizer"> |
201 |
| - {file.name + (/\./.test(file.name) ? '' : `.${file.type}`)} |
202 |
| - </span> |
203 |
| -
|
204 |
| - <!-- svelte-ignore a11y-autofocus --> |
205 |
| - <input |
206 |
| - autofocus |
207 |
| - spellcheck={false} |
208 |
| - bind:value={$files[editing_index].name} |
209 |
| - on:focus={select_input} |
210 |
| - on:blur={close_edit} |
211 |
| - on:keydown={(e) => |
212 |
| - e.key === 'Enter' && !is_file_name_used(file) && e.currentTarget.blur()} |
213 |
| - class:duplicate={is_file_name_used(file)} |
214 |
| - /> |
| 232 | + {:else if filename === editing_name} |
| 233 | + {@const editing_file = $files.find((file) => get_full_filename(file) === editing_name)} |
| 234 | +
|
| 235 | + {#if editing_file} |
| 236 | + <span class="input-sizer"> |
| 237 | + {input_value + (/\./.test(input_value) ? '' : `.${editing_file.type}`)} |
| 238 | + </span> |
| 239 | +
|
| 240 | + <!-- svelte-ignore a11y-autofocus --> |
| 241 | + <input |
| 242 | + autofocus |
| 243 | + spellcheck={false} |
| 244 | + bind:value={input_value} |
| 245 | + on:focus={select_input} |
| 246 | + on:blur={close_edit} |
| 247 | + on:keydown={(e) => |
| 248 | + e.key === 'Enter' && !is_file_name_used(editing_file) && e.currentTarget.blur()} |
| 249 | + class:duplicate={is_file_name_used(editing_file)} |
| 250 | + /> |
| 251 | + {/if} |
215 | 252 | {:else}
|
216 | 253 | <div
|
217 | 254 | class="editable"
|
218 | 255 | title="edit component name"
|
219 |
| - on:click={() => edit_tab(index)} |
220 |
| - on:keyup={(e) => e.key === ' ' && edit_tab(index)} |
| 256 | + on:click={() => edit_tab(file)} |
| 257 | + on:keyup={(e) => e.key === ' ' && edit_tab(file)} |
221 | 258 | >
|
222 | 259 | {file.name}.{file.type}{#if show_modified && file.modified}*{/if}
|
223 | 260 | </div>
|
224 | 261 |
|
225 | 262 | <span
|
226 | 263 | class="remove"
|
227 |
| - on:click={() => remove(index)} |
228 |
| - on:keyup={(e) => e.key === ' ' && remove(index)} |
| 264 | + on:click={() => remove(filename)} |
| 265 | + on:keyup={(e) => e.key === ' ' && remove(filename)} |
229 | 266 | >
|
230 | 267 | <svg width="12" height="12" viewBox="0 0 24 24">
|
231 | 268 | <line stroke="#999" x1="18" y1="6" x2="6" y2="18" />
|
|
0 commit comments