fix: resolve on: events, svelte 5 updates

This commit is contained in:
2026-02-03 22:12:35 +05:00
parent 2b701dec6c
commit f49992b19e
7 changed files with 89 additions and 47 deletions
+19 -5
View File
@@ -1,11 +1,25 @@
<script lang="ts"> <script lang="ts">
import { onMount } from "svelte"; import { onMount } from "svelte";
import Cropper from "cropperjs"; import { browser } from "$app/environment";
import type { ImageCropResult } from "../lib/types/panel"; import type { ImageCropResult } from "../lib/types/panel";
import { ImageService } from "../lib/services/imageService"; import { ImageService } from "../lib/services/imageService";
import { uiStore } from "../stores/uiStore"; import { uiStore } from "../stores/uiStore";
import "cropperjs/dist/cropper.css";
let Cropper: any = $state(undefined);
onMount(async () => {
if (browser) {
const cropperModule = await import("cropperjs");
Cropper = cropperModule.default;
// Импортируем стили только на клиенте
await import("cropperjs/dist/cropper.css");
// Инициализируем cropper после загрузки
initializeCropper();
}
});
let { imageSrc, onCropComplete, onCancel }: { let { imageSrc, onCropComplete, onCancel }: {
imageSrc: string; imageSrc: string;
@@ -30,7 +44,7 @@
}); });
function initializeCropper() { function initializeCropper() {
if (!imageElement) return; if (!imageElement || !Cropper) return;
cropper = new Cropper(imageElement, { cropper = new Cropper(imageElement, {
aspectRatio: NaN, // Allow any aspect ratio aspectRatio: NaN, // Allow any aspect ratio
@@ -113,14 +127,14 @@
<div class="cropper-actions"> <div class="cropper-actions">
<button <button
class="btn btn-secondary" class="btn btn-secondary"
on:click={handleCancel} onclick={handleCancel}
disabled={isProcessing} disabled={isProcessing}
> >
Отмена Отмена
</button> </button>
<button <button
class="btn btn-primary" class="btn btn-primary"
on:click={handleCrop} onclick={handleCrop}
disabled={isProcessing} disabled={isProcessing}
> >
{isProcessing ? "Обработка..." : "Применить"} {isProcessing ? "Обработка..." : "Применить"}
+6 -6
View File
@@ -149,14 +149,14 @@
<div class="uploaded-image-preview"> <div class="uploaded-image-preview">
<img src={uploadedImage} alt="Uploaded preview" /> <img src={uploadedImage} alt="Uploaded preview" />
<div class="preview-actions"> <div class="preview-actions">
<button class="btn btn-secondary" on:click={resetUpload}> Загрузить другое </button> <button class="btn btn-secondary" onclick={resetUpload}> Загрузить другое </button>
</div> </div>
</div> </div>
{:else} {:else}
<div <div
bind:this={dropZone} bind:this={dropZone}
class="drop-zone {isDragOver ? 'drag-over' : ''} {uiError ? 'error' : ''}" class="drop-zone {isDragOver ? 'drag-over' : ''} {uiError ? 'error' : ''}"
on:click={triggerFileInput} onclick={triggerFileInput}
> >
<div class="drop-zone-content"> <div class="drop-zone-content">
<svg class="upload-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"> <svg class="upload-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
@@ -169,15 +169,15 @@
<p>Перетащите файл сюда, нажмите для выбора, или вставьте через Ctrl+V</p> <p>Перетащите файл сюда, нажмите для выбора, или вставьте через Ctrl+V</p>
<div class="upload-methods"> <div class="upload-methods">
<button class="btn btn-primary" on:click={triggerFileInput}> Выбрать файл </button> <button class="btn btn-primary" onclick={triggerFileInput}> Выбрать файл </button>
<button class="btn btn-secondary" on:click={() => (showUrlInput = !showUrlInput)}> <button class="btn btn-secondary" onclick={() => (showUrlInput = !showUrlInput)}>
{showUrlInput ? "Скрыть" : "По URL"} {showUrlInput ? "Скрыть" : "По URL"}
</button> </button>
</div> </div>
{#if showUrlInput} {#if showUrlInput}
<form bind:this={urlForm} on:submit={handleUrlSubmit} class="url-input-form"> <form bind:this={urlForm} onsubmit={handleUrlSubmit} class="url-input-form">
<input <input
bind:this={urlInput} bind:this={urlInput}
type="url" type="url"
@@ -202,7 +202,7 @@
type="file" type="file"
accept="image/jpeg,image/jpg,image/png,image/webp,image/gif" accept="image/jpeg,image/jpg,image/png,image/webp,image/gif"
style="display: none;" style="display: none;"
on:change={(e) => { onchange={(e) => {
const file = e.target.files?.[0]; const file = e.target.files?.[0];
if (file) handleFileUpload(file); if (file) handleFileUpload(file);
}} }}
+3 -3
View File
@@ -75,7 +75,7 @@
<div class="panel-list"> <div class="panel-list">
<div class="panel-list-header"> <div class="panel-list-header">
<h2>Сохраненные панели</h2> <h2>Сохраненные панели</h2>
<button class="btn btn-refresh" on:click={loadPanels} aria-label="Обновить список"> <button class="btn btn-refresh" onclick={loadPanels} aria-label="Обновить список">
</button> </button>
</div> </div>
@@ -95,7 +95,7 @@
{#each panels as panel (panel.id)} {#each panels as panel (panel.id)}
<div <div
class="panel-card {selectedPanelId === panel.id ? 'selected' : ''}" class="panel-card {selectedPanelId === panel.id ? 'selected' : ''}"
on:click={() => handlePanelSelect(panel)} onclick={() => handlePanelSelect(panel)}
role="button" role="button"
tabindex="0" tabindex="0"
> >
@@ -120,7 +120,7 @@
<div class="panel-actions"> <div class="panel-actions">
<button <button
class="btn-icon btn-delete" class="btn-icon btn-delete"
on:click|stopPropagation={() => handlePanelDelete(panel.id)} onclick={(e) => { e.stopPropagation(); handlePanelDelete(panel.id); }}
aria-label="Удалить панель" aria-label="Удалить панель"
> >
🗑️ 🗑️
+9 -6
View File
@@ -1,6 +1,7 @@
<script lang="ts"> <script lang="ts">
import { onMount } from "svelte"; import { onMount } from "svelte";
import { browser } from "$app/environment";
import { panelStore } from "../stores/panelStore"; import { panelStore } from "../stores/panelStore";
import type { Panel } from "../lib/types/panel"; import type { Panel } from "../lib/types/panel";
import type { PageProps } from "./$types"; import type { PageProps } from "./$types";
@@ -25,11 +26,13 @@
}); });
onMount(async () => { onMount(async () => {
const svelteKonva = await import("svelte-konva"); if (browser) {
Stage = svelteKonva.Stage; const svelteKonva = await import("svelte-konva");
Layer = svelteKonva.Layer; Stage = svelteKonva.Stage;
Image = svelteKonva.Image; Layer = svelteKonva.Layer;
Text = svelteKonva.Text; Image = svelteKonva.Image;
Text = svelteKonva.Text;
}
}); });
function loadImage(src: string) { function loadImage(src: string) {
@@ -51,7 +54,7 @@
<div class="preview-header"> <div class="preview-header">
<h2>Предпросмотр панели</h2> <h2>Предпросмотр панели</h2>
{#if currentPanel} {#if currentPanel}
<button class="btn btn-primary" on:click={handleDownload}> <button class="btn btn-primary" onclick={handleDownload}>
Скачать Скачать
</button> </button>
{/if} {/if}
+7 -7
View File
@@ -92,7 +92,7 @@
class="text-input" class="text-input"
maxlength="100" maxlength="100"
/> />
<button class="btn btn-primary" on:click={handleAddText}> <button class="btn btn-primary" onclick={handleAddText}>
Добавить Добавить
</button> </button>
</div> </div>
@@ -108,13 +108,13 @@
{#each currentPanel.texts as textItem (textItem.id)} {#each currentPanel.texts as textItem (textItem.id)}
<div <div
class="text-item {selectedTextId === textItem.id ? 'selected' : ''}" class="text-item {selectedTextId === textItem.id ? 'selected' : ''}"
on:click={() => handleSelectText(textItem.id)} onclick={() => handleSelectText(textItem.id)}
> >
<div class="text-preview"> <div class="text-preview">
<span class="text-content">{textItem.text}</span> <span class="text-content">{textItem.text}</span>
<button <button
class="btn-icon btn-delete" class="btn-icon btn-delete"
on:click|stopPropagation={() => handleRemoveText(textItem.id)} onclick={(e) => { e.stopPropagation(); handleRemoveText(textItem.id); }}
aria-label="Удалить текст" aria-label="Удалить текст"
> >
× ×
@@ -132,7 +132,7 @@
max="72" max="72"
step="1" step="1"
value={textItem.fontSize} value={textItem.fontSize}
on:input={(e) => handleUpdateText(textItem.id, { oninput={(e) => handleUpdateText(textItem.id, {
fontSize: parseInt(e.target.value) fontSize: parseInt(e.target.value)
})} })}
/> />
@@ -146,7 +146,7 @@
<input <input
type="color" type="color"
value={textItem.color} value={textItem.color}
on:input={(e) => handleUpdateText(textItem.id, { oninput={(e) => handleUpdateText(textItem.id, {
color: e.target.value color: e.target.value
})} })}
/> />
@@ -162,7 +162,7 @@
max="320" max="320"
step="1" step="1"
value={textItem.x} value={textItem.x}
on:input={(e) => handleUpdateText(textItem.id, { oninput={(e) => handleUpdateText(textItem.id, {
x: parseInt(e.target.value) x: parseInt(e.target.value)
})} })}
/> />
@@ -179,7 +179,7 @@
max={currentPanel.height} max={currentPanel.height}
step="1" step="1"
value={textItem.y} value={textItem.y}
on:input={(e) => handleUpdateText(textItem.id, { oninput={(e) => handleUpdateText(textItem.id, {
y: parseInt(e.target.value) y: parseInt(e.target.value)
})} })}
/> />
+2 -2
View File
@@ -1,2 +1,2 @@
export const ssr = true; export const ssr = false;
export const prerender = true; export const prerender = false;
+43 -18
View File
@@ -6,13 +6,26 @@
import { uiStore } from "../stores/uiStore"; import { uiStore } from "../stores/uiStore";
import { panelStorage } from "../lib/utils/panelStorage"; import { panelStorage } from "../lib/utils/panelStorage";
import { exportService } from "../lib/services/exportService"; import { exportService } from "../lib/services/exportService";
import ImageUpload from "../components/ImageUpload.svelte";
import ImageCropper from "../components/ImageCropper.svelte";
import TextManager from "../components/TextManager.svelte";
import PanelPreview from "../components/PanelPreview.svelte";
import PanelList from "../components/PanelList.svelte";
import type { Panel } from "../lib/types/panel"; import type { Panel } from "../lib/types/panel";
let ImageUpload: any = $state(undefined);
let ImageCropper: any = $state(undefined);
let TextManager: any = $state(undefined);
let PanelPreview: any = $state(undefined);
let PanelList: any = $state(undefined);
onMount(async () => {
if (browser) {
[ImageUpload, ImageCropper, TextManager, PanelPreview, PanelList] = await Promise.all([
import("../components/ImageUpload.svelte"),
import("../components/ImageCropper.svelte"),
import("../components/TextManager.svelte"),
import("../components/PanelPreview.svelte"),
import("../components/PanelList.svelte")
]);
}
});
let uploadedImage = $state<string | null>(null); let uploadedImage = $state<string | null>(null);
let croppedImage = $state<string | null>(null); let croppedImage = $state<string | null>(null);
let currentPanel = $state<Panel | null>(null); let currentPanel = $state<Panel | null>(null);
@@ -115,7 +128,7 @@
<div class="app-container"> <div class="app-container">
<div class="app-header"> <div class="app-header">
<h1>Twitch Panels Creator</h1> <h1>Twitch Panels Creator</h1>
<button class="btn btn-primary" on:click={handleNewPanel}>Новая панель</button> <button class="btn btn-primary" onclick={handleNewPanel}>Новая панель</button>
</div> </div>
{#if errorMessage} {#if errorMessage}
@@ -127,25 +140,37 @@
<div class="app-content"> <div class="app-content">
<div class="main-section"> <div class="main-section">
{#if $uiStore.currentStep === "upload"} {#if $uiStore.currentStep === "upload"}
<ImageUpload onImageSelect={handleImageUpload} /> {#if ImageUpload}
<svelte:component this={ImageUpload.default} onImageSelect={handleImageUpload} />
{/if}
{:else if $uiStore.currentStep === "crop" && uploadedImage} {:else if $uiStore.currentStep === "crop" && uploadedImage}
<ImageCropper {#if ImageCropper}
imageSrc={uploadedImage} <svelte:component
onCropComplete={handleCropComplete} this={ImageCropper.default}
onCancel={handleCropCancel} imageSrc={uploadedImage}
/> onCropComplete={handleCropComplete}
onCancel={handleCropCancel}
/>
{/if}
{:else if $uiStore.currentStep === "text"} {:else if $uiStore.currentStep === "text"}
<TextManager onTextUpdate={handleTextUpdate} /> {#if TextManager}
<svelte:component this={TextManager.default} onTextUpdate={handleTextUpdate} />
{/if}
{:else if $uiStore.currentStep === "preview"} {:else if $uiStore.currentStep === "preview"}
<PanelPreview onDownload={handleDownload} /> {#if PanelPreview}
<svelte:component this={PanelPreview.default} onDownload={handleDownload} />
{/if}
{/if} {/if}
</div> </div>
<div class="sidebar"> <div class="sidebar">
<PanelList {#if PanelList}
onPanelSelect={handlePanelSelect} <svelte:component
onPanelDelete={handlePanelDelete} this={PanelList.default}
/> onPanelSelect={handlePanelSelect}
onPanelDelete={handlePanelDelete}
/>
{/if}
</div> </div>
</div> </div>
</div> </div>