mirror of
https://github.com/Ku6epXBOCTuK/easy-png-tools.git
synced 2026-11-13 17:08:20 +00:00
feat: upload image on click, tooltip
This commit is contained in:
@@ -155,8 +155,6 @@
|
|||||||
- [ ] просмотр картинки в оригинальном размере, и возможно с зумом отдельных
|
- [ ] просмотр картинки в оригинальном размере, и возможно с зумом отдельных
|
||||||
областей, чтобы просмотреть мелкие артефакты, выбор разных подложек -
|
областей, чтобы просмотреть мелкие артефакты, выбор разных подложек -
|
||||||
черная\белая\цветные\серая для проверки прозрачных картинок
|
черная\белая\цветные\серая для проверки прозрачных картинок
|
||||||
- [ ] при клике на картинку-исходник предложить загрузитрь другой файл (возможно
|
|
||||||
сделать кнопку, которая появляется при ховере на картинке)
|
|
||||||
- [ ] обдумать объединение пар `inputMode + source` и `resultKind + result` в
|
- [ ] обдумать объединение пар `inputMode + source` и `resultKind + result` в
|
||||||
единую структуру, позволит упростить некоторые вещи - например
|
единую структуру, позволит упростить некоторые вещи - например
|
||||||
`sourceValue` \ `resultValue` в `SchemaPreview.svelte`
|
`sourceValue` \ `resultValue` в `SchemaPreview.svelte`
|
||||||
|
|||||||
@@ -122,6 +122,7 @@
|
|||||||
|
|
||||||
--color-main-tint: hct(from var(--brand-main) h 65 55);
|
--color-main-tint: hct(from var(--brand-main) h 65 55);
|
||||||
--color-main-soft: color-mix(in srgb, var(--color-main) 12%, transparent);
|
--color-main-soft: color-mix(in srgb, var(--color-main) 12%, transparent);
|
||||||
|
--color-scrim: color-mix(in srgb, var(--color-background) 65%, transparent);
|
||||||
--color-accent-tint: hct(from var(--brand-alt) h 65 55);
|
--color-accent-tint: hct(from var(--brand-alt) h 65 55);
|
||||||
--color-success-tint: hct(160 65 55);
|
--color-success-tint: hct(160 65 55);
|
||||||
--color-warning-tint: hct(80 65 55);
|
--color-warning-tint: hct(80 65 55);
|
||||||
|
|||||||
@@ -29,6 +29,27 @@
|
|||||||
const sourceUrl = $derived(source ? toDataUrl(source) : null);
|
const sourceUrl = $derived(source ? toDataUrl(source) : null);
|
||||||
|
|
||||||
let dragging = $state(false);
|
let dragging = $state(false);
|
||||||
|
let input = $state<HTMLInputElement | null>(null);
|
||||||
|
// Подсказка замены: показываем на ховер и гасим через 10с до нового захода.
|
||||||
|
let hintVisible = $state(false);
|
||||||
|
let hintTimer: ReturnType<typeof setTimeout> | null = null;
|
||||||
|
|
||||||
|
function showHint() {
|
||||||
|
hintVisible = true;
|
||||||
|
if (hintTimer) clearTimeout(hintTimer);
|
||||||
|
hintTimer = setTimeout(() => {
|
||||||
|
hintVisible = false;
|
||||||
|
hintTimer = null;
|
||||||
|
}, 3_000);
|
||||||
|
}
|
||||||
|
|
||||||
|
function hideHint() {
|
||||||
|
hintVisible = false;
|
||||||
|
if (hintTimer) {
|
||||||
|
clearTimeout(hintTimer);
|
||||||
|
hintTimer = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function onDrop(e: DragEvent) {
|
function onDrop(e: DragEvent) {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
@@ -36,6 +57,10 @@
|
|||||||
const file = e.dataTransfer?.files?.[0];
|
const file = e.dataTransfer?.files?.[0];
|
||||||
if (file) onupload?.(file);
|
if (file) onupload?.(file);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function open() {
|
||||||
|
input?.click();
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<PreviewTile label={t("sourceCard.source")} viewMode={mode}>
|
<PreviewTile label={t("sourceCard.source")} viewMode={mode}>
|
||||||
@@ -52,15 +77,38 @@
|
|||||||
<div
|
<div
|
||||||
class="tile-drop"
|
class="tile-drop"
|
||||||
class:dragging
|
class:dragging
|
||||||
role="region"
|
class:hint={hintVisible}
|
||||||
aria-label={t("dropZone.overlayDefault")}
|
role="button"
|
||||||
|
tabindex="0"
|
||||||
|
aria-label={t("sourceCard.replaceImage")}
|
||||||
|
onclick={open}
|
||||||
|
onkeydown={(e) => {
|
||||||
|
if (e.key === "Enter" || e.key === " ") {
|
||||||
|
e.preventDefault();
|
||||||
|
open();
|
||||||
|
}
|
||||||
|
}}
|
||||||
ondragover={(e) => {
|
ondragover={(e) => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
dragging = true;
|
dragging = true;
|
||||||
}}
|
}}
|
||||||
ondragleave={() => (dragging = false)}
|
ondragleave={() => (dragging = false)}
|
||||||
ondrop={onDrop}
|
ondrop={onDrop}
|
||||||
|
onmouseenter={showHint}
|
||||||
|
onmouseleave={hideHint}
|
||||||
|
onfocus={showHint}
|
||||||
|
onblur={hideHint}
|
||||||
>
|
>
|
||||||
|
<input
|
||||||
|
bind:this={input}
|
||||||
|
type="file"
|
||||||
|
accept="image/*"
|
||||||
|
hidden
|
||||||
|
onchange={(e) => {
|
||||||
|
const file = (e.target as HTMLInputElement).files?.[0];
|
||||||
|
if (file) onupload?.(file);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
<img
|
<img
|
||||||
data-testid="source-image"
|
data-testid="source-image"
|
||||||
src={sourceUrl}
|
src={sourceUrl}
|
||||||
@@ -68,6 +116,8 @@
|
|||||||
/>
|
/>
|
||||||
{#if dragging}
|
{#if dragging}
|
||||||
<span class="drop-hint">{t("dropZone.overlayDefault")}</span>
|
<span class="drop-hint">{t("dropZone.overlayDefault")}</span>
|
||||||
|
{:else}
|
||||||
|
<span class="hover-hint">{t("sourceCard.replaceHint")}</span>
|
||||||
{/if}
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
{:else if mode === "image"}
|
{:else if mode === "image"}
|
||||||
@@ -114,4 +164,38 @@
|
|||||||
text-align: center;
|
text-align: center;
|
||||||
pointer-events: none;
|
pointer-events: none;
|
||||||
}
|
}
|
||||||
|
.tile-drop {
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
.tile-drop::before {
|
||||||
|
content: "";
|
||||||
|
position: absolute;
|
||||||
|
inset: 0;
|
||||||
|
background: var(--color-scrim);
|
||||||
|
opacity: 0;
|
||||||
|
transition: opacity var(--duration-s) ease;
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
.tile-drop.hint::before {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
.tile-drop .hover-hint {
|
||||||
|
position: absolute;
|
||||||
|
inset: 25%;
|
||||||
|
display: grid;
|
||||||
|
place-items: center;
|
||||||
|
padding: var(--space-l);
|
||||||
|
border: var(--size-border-thick) dashed var(--color-main);
|
||||||
|
border-radius: var(--radius-m);
|
||||||
|
background: var(--color-panel);
|
||||||
|
color: var(--color-main);
|
||||||
|
font: 600 var(--font-size-l) var(--font-mono);
|
||||||
|
text-align: center;
|
||||||
|
opacity: 0;
|
||||||
|
transition: opacity var(--duration-s) ease;
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
.tile-drop.hint .hover-hint {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -59,6 +59,7 @@ export const en: Dict = {
|
|||||||
sourceCard: {
|
sourceCard: {
|
||||||
source: "Source",
|
source: "Source",
|
||||||
replaceImage: "Replace image",
|
replaceImage: "Replace image",
|
||||||
|
replaceHint: "Click to upload an image",
|
||||||
chooseImage: "choose an image",
|
chooseImage: "choose an image",
|
||||||
noSource: "no source — configure the parameters",
|
noSource: "no source — configure the parameters",
|
||||||
alt: "Source image",
|
alt: "Source image",
|
||||||
|
|||||||
@@ -60,6 +60,7 @@ export const ru: Dict = {
|
|||||||
sourceCard: {
|
sourceCard: {
|
||||||
source: "Источник",
|
source: "Источник",
|
||||||
replaceImage: "Заменить изображение",
|
replaceImage: "Заменить изображение",
|
||||||
|
replaceHint: "Кликните, чтобы загрузить изображение",
|
||||||
chooseImage: "выберите изображение",
|
chooseImage: "выберите изображение",
|
||||||
noSource: "нет источника — настройте параметры",
|
noSource: "нет источника — настройте параметры",
|
||||||
alt: "Исходное изображение",
|
alt: "Исходное изображение",
|
||||||
|
|||||||
Reference in New Issue
Block a user