Skip to content

Commit 53b4bc9

Browse files
committed
Merge branch 'main' of github.com:sveltejs/svelte.dev
2 parents 5a7d8ff + 8c115ee commit 53b4bc9

File tree

3 files changed

+101
-120
lines changed

3 files changed

+101
-120
lines changed

packages/editor/src/lib/Workspace.svelte.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -206,6 +206,8 @@ export class Workspace {
206206
if (is_file(item)) {
207207
this.#select(item);
208208
this.#onreset?.(this.#files);
209+
210+
this.modified[item.name] = true;
209211
}
210212

211213
return item;
@@ -320,6 +322,11 @@ export class Workspace {
320322
this.#select(new_item as File);
321323
}
322324

325+
if (this.modified[previous.name]) {
326+
delete this.modified[previous.name];
327+
this.modified[name] = true;
328+
}
329+
323330
this.#onreset?.(this.#files);
324331
}
325332

packages/editor/src/lib/codemirror.css

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@
88
height: 100%;
99

1010
&.cm-focused {
11+
outline: none;
12+
1113
.cm-cursor {
1214
border-left-color: var(--sk-fg-3);
1315
}

packages/repl/src/lib/Input/ComponentSelector.svelte

Lines changed: 92 additions & 120 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,8 @@
11
<script lang="ts">
2-
import { forcefocus } from '@sveltejs/site-kit/actions';
3-
import { tick } from 'svelte';
42
import RunesInfo from './RunesInfo.svelte';
53
import Migrate from './Migrate.svelte';
64
import type { Workspace, File } from 'editor';
5+
import { tick } from 'svelte';
76
87
interface Props {
98
runes: boolean;
@@ -14,39 +13,20 @@
1413
1514
let { runes, onchange, workspace, can_migrate }: Props = $props();
1615
17-
let editing_name: string | null = $state(null);
18-
let input_value = $state('');
19-
20-
function select_file(filename: string) {
21-
if (workspace.current.name !== filename) {
22-
editing_name = null;
23-
workspace.select(filename);
24-
}
25-
}
26-
27-
function edit_tab(file: File) {
28-
if (workspace.current.name === file.name) {
29-
editing_name = file.name;
30-
input_value = file.name;
31-
}
32-
}
16+
let input = $state() as HTMLInputElement;
17+
let input_value = $state(workspace.current.name);
3318
34-
async function close_edit() {
35-
if (input_value === editing_name) {
19+
async function close_edit(file: File) {
20+
if (input_value === file.name || input_value === '') {
3621
// nothing to do
37-
editing_name = null;
22+
input_value = file.name;
3823
return;
3924
}
4025
41-
const edited_file = (workspace.files as File[]).find((val) => val.name === editing_name);
42-
if (!edited_file) return; // TODO can this happen?
26+
const deconflicted = deconflict(input_value, file);
4327
44-
const deconflicted = deconflict(input_value, edited_file);
45-
46-
workspace.rename(edited_file, deconflicted);
28+
workspace.rename(file, deconflicted);
4729
workspace.focus();
48-
49-
editing_name = null;
5030
}
5131
5232
function deconflict(name: string, file?: File) {
@@ -70,7 +50,7 @@
7050
onchange();
7151
}
7252
73-
function add_new() {
53+
async function add_new() {
7454
const basename = deconflict(`Component.svelte`);
7555
7656
const file = workspace.add({
@@ -81,10 +61,11 @@
8161
text: true
8262
});
8363
84-
editing_name = file.name;
8564
input_value = file.name;
86-
8765
onchange();
66+
67+
await tick();
68+
input.focus();
8869
}
8970
9071
// drag and drop
@@ -97,16 +78,18 @@
9778
<div class="file-tabs">
9879
{#each workspace.files as File[] as file, index (file.name)}
9980
<div
100-
id={file.name}
10181
class="button"
82+
class:editable={file.name !== 'App.svelte'}
10283
role="button"
10384
tabindex="0"
104-
class:active={file.name === workspace.current.name}
105-
class:draggable={file.name !== editing_name && index !== 0}
85+
aria-current={file === workspace.current}
10686
class:drag-over={file === dragover}
107-
onclick={() => select_file(file.name)}
108-
onkeyup={(e) => e.key === ' ' && select_file(file.name)}
109-
draggable={file.name !== editing_name}
87+
onclick={() => {
88+
workspace.select(file.name);
89+
input_value = file.name;
90+
}}
91+
onkeyup={(e) => e.key === ' ' && workspace.select(file.name)}
92+
draggable="true"
11093
ondragstart={() => (dragging = file)}
11194
ondragover={(e) => (e.preventDefault(), (dragover = file))}
11295
ondragleave={(e) => (e.preventDefault(), (dragover = null))}
@@ -120,42 +103,36 @@
120103
>
121104
<i class="drag-handle"></i>
122105

123-
{#if file.name === 'App.svelte'}
124-
<div class="uneditable">
125-
App.svelte{#if workspace.modified[file.name]}*{/if}
126-
</div>
127-
{:else if file.name === editing_name}
128-
<span class="input-sizer">
129-
<span style="color: transparent">{input_value}</span>
130-
</span>
106+
<span class="filename">
107+
{(file === workspace.current && file.name !== 'App.svelte' ? input_value : file.name) +
108+
(workspace.modified[file.name] ? '*' : '') || ' '}
109+
</span>
131110

111+
{#if file === workspace.current && file.name !== 'App.svelte'}
132112
<!-- svelte-ignore a11y_autofocus -->
133113
<input
134-
use:forcefocus
135114
spellcheck={false}
115+
bind:this={input}
136116
bind:value={input_value}
137117
onfocus={async (event) => {
138118
const input = event.currentTarget;
139-
await tick();
140-
input.select();
119+
setTimeout(() => {
120+
input.select();
121+
});
141122
}}
142-
onblur={close_edit}
123+
onblur={() => close_edit(file)}
143124
onkeydown={(e) => {
144125
if (e.key === 'Enter') {
145126
e.preventDefault();
146127
e.currentTarget.blur();
147128
}
129+
130+
if (e.key === 'Escape') {
131+
input_value = file.name;
132+
e.currentTarget.blur();
133+
}
148134
}}
149135
/>
150-
{:else}
151-
<div
152-
class="editable"
153-
title="edit component name"
154-
onclick={() => edit_tab(file)}
155-
onkeyup={(e) => e.key === ' ' && edit_tab(file)}
156-
>
157-
{file.name}{#if workspace.modified[file.name]}*{/if}
158-
</div>
159136

160137
<span
161138
class="remove"
@@ -166,7 +143,7 @@
166143
}}
167144
onkeyup={(e) => e.key === ' ' && remove_file(file)}
168145
>
169-
<svg width="12" height="12" viewBox="0 0 24 24">
146+
<svg viewBox="0 0 24 24">
170147
<line stroke="#999" x1="18" y1="6" x2="6" y2="18" />
171148
<line stroke="#999" x1="6" y1="6" x2="18" y2="18" />
172149
</svg>
@@ -176,7 +153,11 @@
176153
{/each}
177154
</div>
178155

179-
<button class="add-new" onclick={add_new} aria-label="add new component" title="add new component"
156+
<button
157+
class="raised add-new"
158+
onclick={add_new}
159+
aria-label="add new component"
160+
title="add new component"
180161
></button>
181162

182163
<div class="runes">
@@ -189,6 +170,8 @@
189170
.component-selector {
190171
position: relative;
191172
display: flex;
173+
gap: 0.5rem;
174+
align-items: center;
192175
padding: 0 1rem 0 0;
193176
194177
/* fake border (allows tab borders to appear above it) */
@@ -206,13 +189,13 @@
206189
.file-tabs {
207190
border: none;
208191
margin: 0;
192+
height: 100%;
209193
white-space: nowrap;
210194
overflow-x: auto;
211195
overflow-y: hidden;
212196
}
213197
214-
.file-tabs .button,
215-
.add-new {
198+
.file-tabs .button {
216199
position: relative;
217200
display: inline-flex;
218201
align-items: center;
@@ -221,19 +204,17 @@
221204
border: none;
222205
padding: 0 1rem;
223206
height: 100%;
224-
aspect-ratio: 1;
225-
margin: 0;
226-
border-radius: 0;
227207
cursor: pointer;
228208
}
229209
230-
.add-new {
231-
background: url(./file-new.svg) 50% 50% no-repeat;
232-
background-size: 1em;
233-
}
234-
235210
.file-tabs .button {
236-
padding: 0 1rem 0 2em;
211+
--padding-left: 2.6rem;
212+
--padding-right: 1.4rem;
213+
padding: 0 var(--padding-right) 0 var(--padding-left);
214+
215+
&.editable {
216+
--padding-right: 1.8rem;
217+
}
237218
238219
.drag-handle {
239220
cursor: move;
@@ -246,75 +227,66 @@
246227
background-size: 1em;
247228
}
248229
249-
&.active {
250-
border-bottom: 1px solid var(--sk-fg-accent);
230+
.remove {
231+
position: absolute;
232+
display: none;
233+
top: 0;
234+
right: 0;
235+
padding: 0 0.2rem;
236+
width: 1.6rem;
237+
height: 100%;
238+
cursor: pointer;
239+
240+
svg {
241+
width: 100%;
242+
height: 100%;
243+
}
251244
}
252-
}
253245
254-
.editable,
255-
.uneditable,
256-
.input-sizer,
257-
input {
258-
display: inline-block;
259-
position: relative;
260-
line-height: 1;
246+
&.drag-over {
247+
background: var(--sk-bg-4);
248+
}
249+
250+
&[aria-current='true'] {
251+
border-bottom: 1px solid var(--sk-fg-accent);
252+
253+
&.editable .filename {
254+
cursor: text;
255+
}
256+
257+
.remove {
258+
display: block;
259+
}
260+
}
261261
}
262262
263263
input {
264264
position: absolute;
265-
width: 100%;
265+
width: calc(100% - var(--padding-left) - var(--padding-right));
266266
border: none;
267-
color: var(--sk-fg-accent);
268267
outline: none;
269-
background-color: transparent;
268+
background-color: inherit;
269+
color: inherit;
270270
top: 0;
271-
left: 0;
271+
left: var(--padding-left);
272272
height: 100%;
273273
display: flex;
274274
align-items: center;
275275
justify-content: center;
276276
font-family: var(--sk-font-family-ui);
277277
font: var(--sk-font-ui-small); /* TODO can we just inherit */
278-
padding: 0 1rem 1px 2em;
279278
box-sizing: border-box;
280-
}
281279
282-
.duplicate {
283-
color: var(--sk-fg-accent);
284-
}
285-
286-
.remove {
287-
position: absolute;
288-
display: none;
289-
right: 1px;
290-
top: 4px;
291-
width: 16px;
292-
text-align: right;
293-
padding: 12px 0 12px 5px;
294-
font-size: 8px;
295-
cursor: pointer;
296-
}
297-
298-
.file-tabs .button.active .editable {
299-
cursor: text;
300-
}
301-
302-
.file-tabs .button.active .remove {
303-
display: block;
304-
}
305-
306-
.file-tabs .button.drag-over {
307-
background: #67677814;
308-
}
309-
310-
.file-tabs .button.drag-over {
311-
cursor: move;
280+
&:focus {
281+
color: var(--sk-fg-accent);
282+
}
312283
}
313284
314285
.add-new {
315-
padding: 12px 10px 8px 8px;
316-
height: 40px;
317-
text-align: center;
286+
height: 3.2rem;
287+
aspect-ratio: 1;
288+
background: url(./file-new.svg) 50% 50% no-repeat;
289+
background-size: 1em;
318290
}
319291
320292
.runes {

0 commit comments

Comments
 (0)