refactor: consistent component props

This commit is contained in:
2026-02-04 12:35:31 +05:00
parent aa5c55f95f
commit 2797fea079
13 changed files with 123 additions and 143 deletions
+4 -4
View File
@@ -1,11 +1,11 @@
<script lang="ts">
import { Button } from "../lib/components/ui";
let {
onUploadNewImage
}: {
interface Props {
onUploadNewImage?: () => void;
} = $props();
}
let { onUploadNewImage }: Props = $props();
</script>
<div class="app-header">
+4 -5
View File
@@ -1,13 +1,12 @@
<script lang="ts">
import { Button } from "../lib/components/ui";
let {
backgroundImage,
onUploadNewImage
}: {
interface Props {
backgroundImage?: string | null;
onUploadNewImage: () => void;
} = $props();
}
let { backgroundImage, onUploadNewImage }: Props = $props();
</script>
{#if backgroundImage}
+7 -16
View File
@@ -6,15 +6,13 @@
import { uiStore, setLoading } from "../stores/uiStore";
import { Button } from "../lib/components/ui";
let {
imageSrc,
onCropComplete,
onCancel,
}: {
interface Props {
imageSrc: string;
onCropComplete: (croppedImage: string) => void;
onCancel: () => void;
} = $props();
}
let { imageSrc, onCropComplete, onCancel }: Props = $props();
let imageElement: HTMLImageElement;
let cropper: CropperJS | null = null;
@@ -109,9 +107,7 @@
<div class="cropper-actions">
<Button variant="secondary" onclick={handleCancel} disabled={isProcessing}>Отмена</Button>
<Button variant="primary" onclick={handleCrop} disabled={isProcessing} loading={isProcessing}>
Применить
</Button>
<Button variant="primary" onclick={handleCrop} disabled={isProcessing} loading={isProcessing}>Применить</Button>
</div>
<div class="cropper-info">
@@ -202,11 +198,7 @@
width: 100%;
}
:global(.cropper-wrap-box,
.cropper-canvas,
.cropper-drag-box,
.cropper-crop-box,
.cropper-modal) {
:global(.cropper-wrap-box, .cropper-canvas, .cropper-drag-box, .cropper-crop-box, .cropper-modal) {
bottom: 0;
left: 0;
position: absolute;
@@ -214,8 +206,7 @@
top: 0;
}
:global(.cropper-wrap-box,
.cropper-canvas) {
:global(.cropper-wrap-box, .cropper-canvas) {
overflow: hidden;
}
+5 -9
View File
@@ -2,23 +2,19 @@
import ImageUpload from "./ImageUpload.svelte";
import ImageCropper from "./ImageCropper.svelte";
let {
currentStep,
uploadedImage,
onImageUpload,
onCropComplete,
onCropCancel
}: {
interface Props {
currentStep: string;
uploadedImage: string | null;
onImageUpload: (image: string) => void;
onCropComplete: (croppedImage: string) => void;
onCropCancel: () => void;
} = $props();
}
let { currentStep, uploadedImage, onImageUpload, onCropComplete, onCropCancel }: Props = $props();
</script>
{#if currentStep === "upload"}
<ImageUpload onImageSelect={onImageUpload} />
{:else if currentStep === "crop" && uploadedImage}
<ImageCropper imageSrc={uploadedImage} onCropComplete={onCropComplete} onCancel={onCropCancel} />
<ImageCropper imageSrc={uploadedImage} {onCropComplete} onCancel={onCropCancel} />
{/if}
+16 -4
View File
@@ -9,7 +9,11 @@
let imageService = new ImageService();
let { onImageSelect }: { onImageSelect: (image: string) => void } = $props();
interface Props {
onImageSelect: (image: string) => void;
}
let { onImageSelect }: Props = $props();
let dropZone = $state<HTMLElement | null>(null);
let fileInput = $state<HTMLInputElement | null>(null);
@@ -167,7 +171,7 @@
tabindex="0"
onclick={triggerFileInput}
onkeydown={(e) => {
if (e.key === 'Enter' || e.key === ' ') {
if (e.key === "Enter" || e.key === " ") {
e.preventDefault();
triggerFileInput();
}
@@ -184,7 +188,13 @@
<p>Перетащите файл сюда, нажмите для выбора, или вставьте через Ctrl+V</p>
<div class="upload-methods">
<Button variant="primary" onclick={(e) => { e.stopPropagation(); triggerFileInput(); }}>Выбрать файл</Button>
<Button
variant="primary"
onclick={(e) => {
e.stopPropagation();
triggerFileInput();
}}>Выбрать файл</Button
>
<Button variant="secondary" onclick={() => (showUrlInput = !showUrlInput)}>
{showUrlInput ? "Скрыть" : "По URL"}
@@ -200,7 +210,9 @@
required
class="url-input"
/>
<Button type="submit" variant="primary" disabled={$uiStore.isLoading} loading={$uiStore.isLoading}>Загрузить</Button>
<Button type="submit" variant="primary" disabled={$uiStore.isLoading} loading={$uiStore.isLoading}
>Загрузить</Button
>
</form>
{/if}
+9 -12
View File
@@ -1,4 +1,3 @@
<script lang="ts">
import { onMount } from "svelte";
import { panelStore } from "../stores/panelStore";
@@ -6,10 +5,11 @@
import type { Panel } from "../lib/types/panel";
import { IconButton } from "../lib/components/ui";
let { onPanelSelect, onPanelDelete }: {
interface Props {
onPanelSelect: (panel: Panel) => void;
onPanelDelete: (panelId: string) => void;
} = $props();
}
let { onPanelSelect, onPanelDelete }: Props = $props();
let panels = $state<Panel[]>([]);
let selectedPanelId = $state<string | null>(null);
@@ -76,9 +76,7 @@
<div class="panel-list">
<div class="panel-list-header">
<h2>Сохраненные панели</h2>
<IconButton variant="secondary" onclick={loadPanels} ariaLabel="Обновить список">
</IconButton>
<IconButton variant="secondary" onclick={loadPanels} ariaLabel="Обновить список"></IconButton>
</div>
{#if errorMessage}
@@ -102,11 +100,7 @@
>
<div class="panel-preview">
{#if panel.backgroundImage}
<img
src={panel.backgroundImage}
alt="Panel preview"
class="panel-image"
/>
<img src={panel.backgroundImage} alt="Panel preview" class="panel-image" />
{:else}
<div class="no-image">Нет изображения</div>
{/if}
@@ -121,7 +115,10 @@
<div class="panel-actions">
<IconButton
variant="danger"
onclick={(e) => { e.stopPropagation(); handlePanelDelete(panel.id); }}
onclick={(e) => {
e.stopPropagation();
handlePanelDelete(panel.id);
}}
ariaLabel="Удалить панель"
>
🗑️
+29 -30
View File
@@ -6,13 +6,12 @@
import { Stage, Layer, Image, Text } from "svelte-konva";
import { Button } from "../lib/components/ui";
let {
panel,
onDownload,
}: {
interface Props {
panel: Panel;
onDownload?: () => void;
} = $props();
}
let { panel, onDownload }: Props = $props();
let backgroundImage: HTMLImageElement | undefined = $state(undefined);
@@ -63,33 +62,33 @@
<div class="panel-title">{panel.texts[0]?.text || "Без названия"}</div>
<Button variant="primary" size="sm" onclick={onDownload}>Скачать</Button>
</div>
<div class="preview-sections">
<!-- Превью с текстом -->
<div class="preview-section">
<div class="canvas-container">
<Stage width={320} height={panel.height}>
<Layer>
{#if backgroundImage}
<Image image={backgroundImage} width={320} height={panel.height} />
{/if}
{#each panel.texts || [] as textItem (textItem.id)}
{@const textPosition = getTextPosition(textItem)}
<Text
text={textItem.text}
fontSize={textItem.fontSize}
fill={textItem.color}
fontFamily={textItem.fontFamily}
x={textPosition.x}
y={textPosition.y}
align={textItem.textAlign}
width={320 - textItem.paddingX * 2}
/>
{/each}
</Layer>
</Stage>
</div>
<div class="preview-sections">
<!-- Превью с текстом -->
<div class="preview-section">
<div class="canvas-container">
<Stage width={320} height={panel.height}>
<Layer>
{#if backgroundImage}
<Image image={backgroundImage} width={320} height={panel.height} />
{/if}
{#each panel.texts || [] as textItem (textItem.id)}
{@const textPosition = getTextPosition(textItem)}
<Text
text={textItem.text}
fontSize={textItem.fontSize}
fill={textItem.color}
fontFamily={textItem.fontFamily}
x={textPosition.x}
y={textPosition.y}
align={textItem.textAlign}
width={320 - textItem.paddingX * 2}
/>
{/each}
</Layer>
</Stage>
</div>
</div>
</div>
</div>
<style>
+4 -10
View File
@@ -3,15 +3,12 @@
import PanelPreview from "./PanelPreview.svelte";
import { Button } from "../lib/components/ui";
let {
panels,
onDownload,
onDownloadAll
}: {
interface Props {
panels: Panel[];
onDownload: (panel: Panel) => void;
onDownloadAll: () => void;
} = $props();
}
let { panels, onDownload, onDownloadAll }: Props = $props();
</script>
{#if panels.length > 0}
@@ -23,10 +20,7 @@
<div class="panels-list">
{#each panels as panel (panel.id)}
<div class="panel-item">
<PanelPreview
panel={panel}
onDownload={() => onDownload(panel)}
/>
<PanelPreview {panel} onDownload={() => onDownload(panel)} />
</div>
{/each}
</div>
+12 -15
View File
@@ -1,13 +1,14 @@
<script lang="ts">
import { Button } from "../lib/components/ui";
let { onTextAdd, onTextUpdate, onTextDelete, texts }: {
interface Props {
onTextAdd: (text: string) => void;
onTextUpdate: (id: string, text: string) => void;
onTextDelete: (id: string) => void;
texts: Array<{ id: string; text: string }>;
} = $props();
}
let { onTextAdd, onTextUpdate, onTextDelete, texts }: Props = $props();
let newText = $state("");
let errorMessage = $state<string | null>(null);
@@ -107,12 +108,7 @@
class="text-edit-input"
maxlength="100"
/>
<Button
variant="danger"
size="sm"
onclick={() => handleDeleteText(textItem.id)}
aria-label="Удалить текст"
>
<Button variant="danger" size="sm" onclick={() => handleDeleteText(textItem.id)} aria-label="Удалить текст">
×
</Button>
</div>
@@ -125,7 +121,7 @@
<div class="common-settings-section">
<h3>Общие настройки текста</h3>
<p class="settings-note">Настройки применятся ко всем создаваемым панелям</p>
<div class="settings-grid">
<div class="control-group">
<label>
@@ -180,7 +176,7 @@
<button
class="align-btn {commonTextSettings.textAlign === 'left' ? 'active' : ''}"
onclick={() => {
commonTextSettings.textAlign = 'left';
commonTextSettings.textAlign = "left";
}}
aria-label="Выровнять по левому краю"
>
@@ -189,7 +185,7 @@
<button
class="align-btn {commonTextSettings.textAlign === 'center' ? 'active' : ''}"
onclick={() => {
commonTextSettings.textAlign = 'center';
commonTextSettings.textAlign = "center";
}}
aria-label="Выровнять по центру"
>
@@ -198,7 +194,7 @@
<button
class="align-btn {commonTextSettings.textAlign === 'right' ? 'active' : ''}"
onclick={() => {
commonTextSettings.textAlign = 'right';
commonTextSettings.textAlign = "right";
}}
aria-label="Выровнять по правому краю"
>
@@ -238,12 +234,13 @@
commonTextSettings.verticalOffset = parseInt(e.target.value);
}}
/>
<span class="value-display">{commonTextSettings.verticalOffset > 0 ? '+' : ''}{commonTextSettings.verticalOffset}px</span>
<span class="value-display"
>{commonTextSettings.verticalOffset > 0 ? "+" : ""}{commonTextSettings.verticalOffset}px</span
>
</label>
</div>
</div>
</div>
</div>
<style>
+4 -5
View File
@@ -3,13 +3,12 @@
import { panelStore } from "../stores/panelStore";
import { Stage, Layer, Image, Text } from "svelte-konva";
let {
textItem,
height,
}: {
interface Props {
textItem: TextItem;
height: number;
} = $props();
}
let { textItem, height }: Props = $props();
let bgImage: HTMLImageElement | undefined = $state(undefined);
let currentPanel = $state($panelStore);
+5 -13
View File
@@ -1,27 +1,19 @@
<script lang="ts">
import TextManager from "./TextManager.svelte";
let {
texts,
onTextAdd,
onTextUpdate,
onTextDelete
}: {
interface Props {
texts: Array<{ id: string; text: string }>;
onTextAdd: (text: string) => void;
onTextUpdate: (id: string, text: string) => void;
onTextDelete: (id: string) => void;
} = $props();
}
let { texts, onTextAdd, onTextUpdate, onTextDelete }: Props = $props();
</script>
<div class="text-section">
<h2>Добавьте тексты для панелей</h2>
<TextManager
onTextAdd={onTextAdd}
onTextUpdate={onTextUpdate}
onTextDelete={onTextDelete}
texts={texts}
/>
<TextManager {onTextAdd} {onTextUpdate} {onTextDelete} {texts} />
</div>
<style>
+13 -11
View File
@@ -3,6 +3,18 @@
type ButtonSize = "sm" | "md" | "lg";
type ButtonType = "button" | "submit" | "reset";
interface Props {
variant?: ButtonVariant;
size?: ButtonSize;
disabled?: boolean;
type?: ButtonType;
fullWidth?: boolean;
loading?: boolean;
class?: string;
children?: any;
[key: string]: any;
}
let {
variant = "primary",
size = "md",
@@ -13,17 +25,7 @@
class: className = "",
children,
...restProps
}: {
variant?: ButtonVariant;
size?: ButtonSize;
disabled?: boolean;
type?: ButtonType;
fullWidth?: boolean;
loading?: boolean;
class?: string;
children?: any;
[key: string]: any;
} = $props();
}: Props = $props();
</script>
<button
+11 -9
View File
@@ -2,6 +2,16 @@
type IconButtonVariant = "primary" | "secondary" | "danger";
type IconButtonSize = "sm" | "md" | "lg";
interface Props {
variant?: IconButtonVariant;
size?: IconButtonSize;
disabled?: boolean;
ariaLabel?: string;
class?: string;
children?: any;
[key: string]: any;
}
let {
variant = "secondary",
size = "md",
@@ -10,15 +20,7 @@
class: className = "",
children,
...restProps
}: {
variant?: IconButtonVariant;
size?: IconButtonSize;
disabled?: boolean;
ariaLabel?: string;
class?: string;
children?: any;
[key: string]: any;
} = $props();
}: Props = $props();
</script>
<button