Redesign icon input
This commit is contained in:
4 files changed
+141
-26
No files matched your search
@@ -301,6 +301,17 @@
|
||||
"string": "Downloads will appear here"
|
||||
}
|
||||
},
|
||||
"fileInput": {
|
||||
"select": {
|
||||
"string": "Select or drop file"
|
||||
},
|
||||
"clear": {
|
||||
"string": "Clear Selection"
|
||||
},
|
||||
"selected": {
|
||||
"string": "Selected {file}"
|
||||
}
|
||||
},
|
||||
"nwjs": {
|
||||
"furtherStepsWin": {
|
||||
"string": "Further steps for Windows"
|
||||
|
||||
@@ -1,20 +0,0 @@
|
||||
<script>
|
||||
export let file;
|
||||
export let accept;
|
||||
let fileInput;
|
||||
|
||||
const handleChange = (e) => {
|
||||
const files = e.target.files;
|
||||
if (files.length) {
|
||||
file = files[0];
|
||||
} else {
|
||||
file = null;
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<input bind:this={fileInput} type="file" on:change={handleChange} accept={accept}>
|
||||
|
||||
{#if file && fileInput.files.length === 0}
|
||||
(Selected {file.name} from previous visit)
|
||||
{/if}
|
||||
@@ -0,0 +1,126 @@
|
||||
<script>
|
||||
import {_} from '../locales';
|
||||
|
||||
export let file;
|
||||
let fileInput;
|
||||
let dragging;
|
||||
|
||||
$: url = file && URL.createObjectURL(file);
|
||||
$: if (!file && url) {
|
||||
URL.revokeObjectURL(url);
|
||||
url = null;
|
||||
}
|
||||
|
||||
const ACCEPT = [
|
||||
'.png',
|
||||
'.jpg',
|
||||
'.jpeg',
|
||||
'.bmp',
|
||||
'.svg'
|
||||
];
|
||||
|
||||
const handleChange = (e) => {
|
||||
const files = e.target.files;
|
||||
file = files.length ? files[0] : null;
|
||||
};
|
||||
|
||||
const clear = (e) => {
|
||||
e.stopPropagation();
|
||||
file = null;
|
||||
};
|
||||
|
||||
const handleClickBackground = () => {
|
||||
fileInput.click();
|
||||
};
|
||||
|
||||
const handleDragOver = (e) => {
|
||||
if (!e.dataTransfer.types.includes('Files')) {
|
||||
return;
|
||||
}
|
||||
e.preventDefault();
|
||||
dragging = true;
|
||||
};
|
||||
|
||||
const handleDragLeave = (e) => {
|
||||
e.preventDefault();
|
||||
dragging = false;
|
||||
};
|
||||
|
||||
const handleDrop = (e) => {
|
||||
e.preventDefault();
|
||||
dragging = false;
|
||||
if (e.dataTransfer.types.includes('Files')) {
|
||||
const droppedFile = e.dataTransfer.files[0];
|
||||
if (droppedFile && ACCEPT.some((ext) => droppedFile.name.endsWith(ext))) {
|
||||
file = droppedFile;
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style>
|
||||
.container {
|
||||
color: #555;
|
||||
width: 100%;
|
||||
box-sizing: border-box;
|
||||
border: 5px dashed currentColor;
|
||||
transition: .2s border-color, .2s color;
|
||||
border-radius: 20px;
|
||||
min-height: 100px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
overflow: hidden;
|
||||
user-select: none;
|
||||
position: relative;
|
||||
cursor: pointer;
|
||||
}
|
||||
:global([theme="dark"]) .container {
|
||||
color: #999;
|
||||
}
|
||||
.dragging,
|
||||
.container:focus {
|
||||
color: rgb(79, 123, 211);
|
||||
}
|
||||
:global([theme="dark"]) .dragging,
|
||||
:global([theme="dark"]) .container:focus {
|
||||
color: rgb(178, 195, 228);
|
||||
}
|
||||
.placeholder {
|
||||
font-size: 1.5em;
|
||||
}
|
||||
input[type=file] {
|
||||
display: none;
|
||||
}
|
||||
.selected {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
.selected > *:not(:last-child) {
|
||||
margin-right: 12px;
|
||||
}
|
||||
</style>
|
||||
|
||||
<div
|
||||
class="container"
|
||||
class:dragging
|
||||
tabindex="0"
|
||||
on:click={handleClickBackground}
|
||||
on:dragover={handleDragOver}
|
||||
on:dragleave={handleDragLeave}
|
||||
on:drop={handleDrop}
|
||||
>
|
||||
<input bind:this={fileInput} type="file" accept={ACCEPT.join(',')} on:change={handleChange}>
|
||||
{#if file}
|
||||
<div class="selected">
|
||||
<!-- TODO: consider display more sizes so people can see what their icon looks like when small -->
|
||||
<img src={url} alt="Project icon" width="64" height="64">
|
||||
<div>{$_('fileInput.selected').replace('{file}', file.name)}</div>
|
||||
<button on:click={clear}>{$_('fileInput.clear')}</button>
|
||||
</div>
|
||||
{:else}
|
||||
<div class="placeholder">{$_('fileInput.select')}</div>
|
||||
{/if}
|
||||
</div>
|
||||
@@ -4,7 +4,7 @@
|
||||
import Section from './Section.svelte';
|
||||
import Button from './Button.svelte';
|
||||
import ComplexMessage from './ComplexMessage.svelte';
|
||||
import FileInput from './FileInput.svelte';
|
||||
import ImageInput from './ImageInput.svelte';
|
||||
import CustomExtensions from './CustomExtensions.svelte';
|
||||
import Packager from './packager';
|
||||
import writablePersistentStore from './persistent-store';
|
||||
@@ -232,12 +232,10 @@
|
||||
{$_('options.loadingScreenText')}
|
||||
<input type="text" bind:value={$options.loadingScreen.text} placeholder={$_('options.loadingScreenTextPlaceholder')}>
|
||||
</label>
|
||||
<!-- Ignore because FileInput will have an <input> inside it -->
|
||||
<!-- svelte-ignore a11y-label-has-associated-control -->
|
||||
<label>
|
||||
<div>
|
||||
{$_('options.icon')}
|
||||
<FileInput bind:file={$icon} accept=".png,.jpg,.jpeg,.bmp,.svg" />
|
||||
</label>
|
||||
<ImageInput bind:file={$icon} />
|
||||
</div>
|
||||
|
||||
<h3>{$_('options.controls')}</h3>
|
||||
<label>
|
||||
|
||||
Reference in new issue
Block a user