fix: added proper file name handling
Renaming/creating a file with an invalid name should no longer create an empty file in the editor
mellbacon committed
Oct 8, 2023 at 20:30 UTC
0fb423e437fe3c80dc3efc51b1777c382ff83560
8 files changed
+73
-21
src/App.svelte
+1
-1
@@ -68,7 +68,7 @@
68
}
69
</script>
70
<script lang="ts" context="module">
71
- const _openPopup = writable(false);
71
+ export const _openPopup = writable(false);
72
const popupProps = writable({});
73
const popup = writable(null);
74
export const fullscreen = writable(false);
src/config/commands.ts
+1
-1
@@ -196,7 +196,7 @@ export const commands = {
196
openRenameModal(`Rename ${filename}`,
197
`Give a new name to ${filename}`, [
198
{name: "Rename", action: async (name) => {await renameFile(name, oldpath)}},
199
- {name: "Cancel", action: () => {}}
199
+ {name: "Cancel", cancel: true, action: () => {}}
200
])
201
}
202
},
src/lib/FileTree.svelte
+2
-2
@@ -30,12 +30,12 @@
30
{name: "New Folder...", shortcut: "", action: () => {openInputModal("Create New Folder",
31
`Create a new folder in ${path}`, [
32
{name: "Create Folder", action: async (name) => { await createFolder(`${path}${p.sep}${name}`)}},
33
- {name: "Cancel", action: () => {}}
33
+ {name: "Cancel", cancel: true, action: () => {}}
34
], {label: "Folder Name"})}},
35
{name: "New File...", shortcut: "", action: () => {openInputModal("Create New File",
36
`Create a new file in ${path}`, [
37
{name: "Create File", action: (name) => {createFile(`${path}${p.sep}${name}`)}},
38
- {name: "Cancel", action: () => {}}
38
+ {name: "Cancel", cancel: true, action: () => {}}
39
], {label: "File Name"})}},
40
{name: "Copy", shortcut: "Ctrl + C", action: () => {console.warn("Feature not implemented yet.")}},
41
{name: "Cut", disabled: true, shortcut: "Ctrl + X", action: () => {console.warn("Feature not implemented yet.")}},
src/lib/FileTree/FileTreeList.svelte
+2
-2
@@ -42,12 +42,12 @@
42
{name: "New Folder...", shortcut: "", action: () => {openInputModal("Create New Folder",
43
`Create a new folder in ${path}`, [
44
{name: "Create Folder", action: async (name) => { await createFolder(`${path}${p.sep}${name}`)}},
45
- {name: "Cancel", action: () => {}}
45
+ {name: "Cancel", cancel: true, action: () => {}}
46
], {label: "Folder Name"})}},
47
{name: "New File...", shortcut: "", action: () => {openInputModal("Create New File",
48
`Create a new file in ${path}`, [
49
{name: "Create File", action: (name) => {createFile(`${path}${p.sep}${name}`)}},
50
- {name: "Cancel", action: () => {}}
50
+ {name: "Cancel", cancel: true, action: () => {}}
51
], {label: "File Name"})}},
52
{name: "Copy", shortcut: "Ctrl + C", action: () => {console.warn("Feature not implemented yet.")}},
53
{name: "Cut", disabled: isroot, shortcut: "Ctrl + X", action: () => {console.warn("Feature not implemented yet.")}},
src/lib/Modals/InputModal.svelte
+20
-8
@@ -2,13 +2,16 @@
2
import { onMount } from "svelte";
3
import Input from "../utility/Input.svelte";
4
import Popup from "../utility/Popup.svelte";
5
+ import { _openPopup } from "../../App.svelte";
6
+ import { checkValidFileName } from "../File";
7
8
export let title;
9
export let buttons;
10
export let description = "";
11
export let options = {label: null, placeholder: "Enter text..."};
10
- export let open = false;
11
-
12
+ let invalid = false;
13
+ let helpText = ""
14
+ let invalidText = "";
15
16
onMount(() => {
17
if (!options.placeholder) {
@@ -17,17 +20,26 @@
20
})
21
22
let value = "";
20
- async function handleButtonClick(buttonAction) {
23
+ async function handleButtonClick(buttonAction, cancel) {
24
await buttonAction(value);
22
- open = false;
25
+ if (!invalid || cancel) {
26
+ _openPopup.set(false);
27
+ }
28
}
29
30
</script>
26
-<Popup bind:open {title} {description}>
27
- <Input bind:value label={options.label} placeholder={options.placeholder} />
31
+<Popup bind:open={$_openPopup} {title} {description}>
32
+ <Input bind:value bind:invalid label={options.label} placeholder={options.placeholder} hintText={helpText} invalidText={invalidText} />
33
<svelte:fragment slot="buttons">
34
{#each buttons as button}
30
- <button on:click={async () => {await handleButtonClick(button.action)}}>{button.name}</button>
35
+ <button on:click={async () => {
36
+ if (!button.cancel && (!checkValidFileName(value) || value === undefined || !value)) {
37
+ invalid = true;
38
+ invalidText = `{${value === "" ? "null" : value}} is not a valid file or directory name`;
39
+ return;
40
+ }
41
+ await handleButtonClick(button.action, button.cancel);
42
+ }}>{button.name}</button>
43
{/each}
44
</svelte:fragment>
33
-</Popup>
\ No newline at end of file
45
+</Popup>
src/lib/Modals/RenameModal.svelte
+19
-6
@@ -2,12 +2,16 @@
2
import { onMount } from "svelte";
3
import Input from "../utility/Input.svelte";
4
import Popup from "../utility/Popup.svelte";
5
+ import { _openPopup } from "../../App.svelte";
6
+ import { checkValidFileName } from "../File";
7
8
export let title;
9
export let buttons;
10
export let description = "";
11
export let options = {label: "Name: ", placeholder: "Enter text..."};
10
- export let open = false;
12
+ let invalid = false;
13
+ let helpText = ""
14
+ let invalidText = "";
15
16
onMount(() => {
17
if (!options.placeholder) {
@@ -16,21 +20,30 @@
20
})
21
22
let value = "";
19
- async function handleButtonClick(buttonAction) {
23
+ async function handleButtonClick(buttonAction, cancel) {
24
await buttonAction(value);
21
- open = false;
25
+ if (!invalid || cancel) {
26
+ _openPopup.set(false);
27
+ }
28
}
29
30
</script>
25
-<Popup bind:open {title} {description}>
31
+<Popup bind:open={$_openPopup} {title} {description}>
32
<div class="rename-input">
27
- <Input bind:value label={"File Name"} placeholder={"Enter name..."} />
33
+ <Input bind:value bind:invalid label={"File Name"} placeholder={"Enter name..."} hintText={helpText} invalidText={invalidText} />
34
<div class="divider"></div>
35
<Input label={"File Type"} placeholder={"-"} readonly _class="ext" />
36
</div>
37
<svelte:fragment slot="buttons">
38
{#each buttons as button}
33
- <button on:click={async () => {await handleButtonClick(button.action)}}>{button.name}</button>
39
+ <button on:click={async () => {
40
+ if (!button.cancel && (!checkValidFileName(value) || value === undefined || !value)) {
41
+ invalid = true;
42
+ invalidText = `{${value === "" ? "null" : value}} is not a valid file name`;
43
+ return;
44
+ }
45
+ await handleButtonClick(button.action, button.cancel);
46
+ }}>{button.name}</button>
47
{/each}
48
</svelte:fragment>
49
</Popup>
src/lib/utility/Input.svelte
+25
-1
@@ -5,15 +5,19 @@
5
export let readonly = false;
6
export let label = null;
7
export let placeholder = "";
8
+ export let hintText = "";
9
+ export let invalidText = "";
10
11
export let value = "";
12
export let _class = "";
13
export let extra_small = false;
14
export let medium = false;
15
+ export let invalid = false;
16
17
let _ = null;
18
async function handleInput(e) {
19
clearTimeout(_);
20
+ invalid = false;
21
_ = setTimeout(() => {
22
dispatch("d_input", {value: value})
23
}, 500);
@@ -25,10 +29,23 @@
29
<label for="textInput">{label}:</label>
30
{/if}
31
28
- <input on:input={handleInput} class="mousetrap" class:medium class:extra_small class:readonly bind:value type="text" name="textInput" id="textInput" readonly={readonly} placeholder={placeholder}>
32
+ <input on:input={handleInput}
33
+ class="mousetrap"
34
+ class:invalid
35
+ class:medium
36
+ class:extra_small
37
+ class:readonly
38
+ bind:value type="text" name="textInput" id="textInput" readonly={readonly} placeholder={placeholder}>
39
+ {#if hintText && !invalid}
40
+ <div class="hint-text" >{hintText}</div>
41
+ {/if}
42
+ {#if invalidText && invalid}
43
+ <div class="hint-text" class:invalid>{invalidText}</div>
44
+ {/if}
45
</div>
46
47
<style lang="scss">
48
+ $invalid-color: #fa4d56;
49
.input-container {
50
display: flex;
51
flex-direction: column;
@@ -50,6 +67,13 @@
67
padding: 0.6em 0.4em 0.6em 0.8em;
68
width: 100%;
69
}
70
+ .hint-text {
71
+ padding: 0 2px;
72
+ font-size: 0.9rem;
73
+ &.invalid {
74
+ color: $invalid-color;
75
+ }
76
+ }
77
.readonly {
78
cursor: not-allowed;
79
background-color: #2d2d2d;
src/theme.scss
+3
@@ -104,6 +104,9 @@ input {
104
border-bottom: 1px solid var(--window-inputBorder) !important;
105
background-color: var(--window-inputBackground);
106
color: var(--window-inputForeground);
107
+ &.invalid {
108
+ border-bottom: 1px solid #fa4d56 !important;
109
+ }
110
}
111
112
.divider {