mirror of
https://github.com/Ku6epXBOCTuK/easy-png-tools.git
synced 2026-11-13 17:08:20 +00:00
refactor: group props in view models
This commit is contained in:
@@ -67,13 +67,15 @@ handleFiles, эффекты, workspace-разметка). Декомпозици
|
||||
Переработка самого паттерна `$effect(() => init())` — часть H1, отдельно не
|
||||
делалась.
|
||||
|
||||
### M3. `SchemaPreview.svelte` — интерфейс из ~40 пропсов
|
||||
### M3. `SchemaPreview.svelte` — интерфейс из ~40 пропсов — СДЕЛАНО (2026-10)
|
||||
|
||||
`web/src/lib/components/schema/SchemaPreview.svelte:22-103`: весь стейт
|
||||
оркестратора проброшен вниз по одному (`stepResults`, `stepMaskable`,
|
||||
`stepMaskOn`, `stepMasks`, `format`, `quality`, `limitKb`, `alphaLoss` + 11
|
||||
колбэков `on*`). Нужен группирующий view-model-объект или разбиение на
|
||||
подкомпоненты.
|
||||
Пропсы сгруппированы в 4 view-model объекта по внутреннему потребителю: `head`
|
||||
(SchemaActions), `source` (SchemaSourceTile), `steps` (промежуточные тайлы),
|
||||
`result` (SchemaResultTile). Интерфейсы — в `schema-preview-model.ts`
|
||||
(`PreviewHeadModel` и др.); имена полей зеркалят пропсы потребителей, поэтому
|
||||
внутри работает спред (`<SchemaActions {...head}>`). Плоскими остались 7
|
||||
идентифицирующих пропсов (inputMode, resultKind, toolId, running, error,
|
||||
aligned, ontogglealign).
|
||||
|
||||
### M4. `runMask` обходил санитизацию параметров — СДЕЛАНО (2026-10)
|
||||
|
||||
|
||||
@@ -1,5 +1,11 @@
|
||||
<script lang="ts">
|
||||
import { hasPreviewResult } from "./schema-preview-model";
|
||||
import {
|
||||
hasPreviewResult,
|
||||
type PreviewHeadModel,
|
||||
type PreviewResultModel,
|
||||
type PreviewSourceModel,
|
||||
type PreviewStepsModel,
|
||||
} from "./schema-preview-model";
|
||||
import SchemaActions from "./SchemaActions.svelte";
|
||||
import PartsGrid from "./PartsGrid.svelte";
|
||||
import SchemaResultTile from "./SchemaResultTile.svelte";
|
||||
@@ -9,101 +15,50 @@
|
||||
import ChipButton from "$lib/components/ui/ChipButton.svelte";
|
||||
import { toDataUrl } from "$lib/core/io";
|
||||
import type { PixelImage } from "$lib/core/types";
|
||||
import type { OutputMime } from "$lib/core/io";
|
||||
import { t } from "$lib/i18n/t";
|
||||
import type {
|
||||
FileResult,
|
||||
InputMode,
|
||||
ResultKind,
|
||||
ResultNote,
|
||||
ToolImageFile,
|
||||
} from "$lib/registry";
|
||||
import type { InputMode, ResultKind } from "$lib/registry";
|
||||
|
||||
interface Props {
|
||||
inputMode: InputMode;
|
||||
resultKind?: ResultKind;
|
||||
toolId: string;
|
||||
source: PixelImage | null;
|
||||
sources?: ToolImageFile[];
|
||||
fileCount?: number;
|
||||
result: PixelImage | null;
|
||||
resultNote?: ResultNote | null;
|
||||
fileResult?: FileResult | null;
|
||||
textSource?: string;
|
||||
textResult?: string | null;
|
||||
textVars?: Record<string, string | number>;
|
||||
mask?: PixelImage | null;
|
||||
maskOn?: boolean;
|
||||
ontogglemask?: () => void;
|
||||
running?: boolean;
|
||||
error?: string;
|
||||
stepResults?: (PixelImage | null)[];
|
||||
stepFileSets?: ToolImageFile[][];
|
||||
stepMaskable?: boolean[];
|
||||
stepMaskOn?: boolean[];
|
||||
stepMasks?: (PixelImage | null)[];
|
||||
ontogglestepmask?: (i: number) => void;
|
||||
aligned?: boolean;
|
||||
ontogglealign?: () => void;
|
||||
format?: OutputMime;
|
||||
quality?: number;
|
||||
limitKb?: number;
|
||||
alphaLoss?: boolean;
|
||||
onupload: (file: File) => void;
|
||||
onuploadmany?: (files: File[]) => void;
|
||||
ontextsource?: (text: string) => void;
|
||||
onrendertext?: () => void;
|
||||
oncopytext?: () => void;
|
||||
ondownloadtxt?: () => void;
|
||||
ondownload: () => void;
|
||||
onformat?: (mime: OutputMime) => void;
|
||||
onquality?: (value: number) => void;
|
||||
onlimit?: (kb: number | undefined) => void;
|
||||
head: PreviewHeadModel;
|
||||
source: PreviewSourceModel;
|
||||
steps?: PreviewStepsModel;
|
||||
result: PreviewResultModel;
|
||||
}
|
||||
let {
|
||||
inputMode,
|
||||
resultKind = "image",
|
||||
toolId,
|
||||
source,
|
||||
sources = [],
|
||||
fileCount = 0,
|
||||
result,
|
||||
resultNote = null,
|
||||
fileResult = null,
|
||||
textSource = "",
|
||||
textResult = null,
|
||||
textVars = undefined,
|
||||
mask = null,
|
||||
maskOn = false,
|
||||
ontogglemask,
|
||||
running = false,
|
||||
error = "",
|
||||
stepResults = [],
|
||||
stepFileSets = [],
|
||||
stepMaskable = [],
|
||||
stepMaskOn = [],
|
||||
stepMasks = [],
|
||||
ontogglestepmask,
|
||||
aligned = false,
|
||||
ontogglealign,
|
||||
format = "image/png",
|
||||
quality = undefined,
|
||||
limitKb = undefined,
|
||||
alphaLoss = false,
|
||||
onupload,
|
||||
onuploadmany,
|
||||
ontextsource,
|
||||
onrendertext,
|
||||
oncopytext,
|
||||
ondownloadtxt,
|
||||
ondownload,
|
||||
onformat,
|
||||
onquality,
|
||||
onlimit,
|
||||
head,
|
||||
source,
|
||||
steps = {},
|
||||
result,
|
||||
}: Props = $props();
|
||||
|
||||
const stepResults = $derived(steps.results ?? []);
|
||||
const stepFileSets = $derived(steps.fileSets ?? []);
|
||||
const stepMaskable = $derived(steps.maskable ?? []);
|
||||
const stepMaskOn = $derived(steps.maskOn ?? []);
|
||||
const stepMasks = $derived(steps.masks ?? []);
|
||||
|
||||
const hasResult = $derived(
|
||||
hasPreviewResult({ inputMode, resultKind, result, fileResult, textResult }),
|
||||
hasPreviewResult({
|
||||
inputMode,
|
||||
resultKind,
|
||||
result: result.result,
|
||||
fileResult: result.fileResult ?? null,
|
||||
textResult: result.textResult ?? null,
|
||||
}),
|
||||
);
|
||||
</script>
|
||||
|
||||
@@ -124,15 +79,7 @@
|
||||
{resultKind}
|
||||
canDownload={hasResult}
|
||||
{running}
|
||||
{format}
|
||||
{quality}
|
||||
{limitKb}
|
||||
{alphaLoss}
|
||||
{onupload}
|
||||
{ondownload}
|
||||
{onformat}
|
||||
{onquality}
|
||||
{onlimit}
|
||||
{...head}
|
||||
/>
|
||||
</div>
|
||||
|
||||
@@ -143,15 +90,9 @@
|
||||
<div class="pair">
|
||||
<SchemaSourceTile
|
||||
mode={inputMode}
|
||||
{source}
|
||||
{sources}
|
||||
{textSource}
|
||||
{running}
|
||||
{fileCount}
|
||||
ontextinput={ontextsource}
|
||||
{onrendertext}
|
||||
{onupload}
|
||||
{onuploadmany}
|
||||
onupload={head.onupload}
|
||||
{...source}
|
||||
/>
|
||||
{#each stepResults.slice(0, -1) as img, i (i)}
|
||||
{@const set = stepFileSets[i] ?? []}
|
||||
@@ -167,7 +108,7 @@
|
||||
<ChipButton
|
||||
label={t("resultCard.maskToggle")}
|
||||
active={stepMaskOn[i] ?? false}
|
||||
onclick={() => ontogglestepmask?.(i)}
|
||||
onclick={() => steps.ontogglestepmask?.(i)}
|
||||
/>
|
||||
{/if}
|
||||
{/snippet}
|
||||
@@ -184,7 +125,7 @@
|
||||
<ChipButton
|
||||
label={t("resultCard.maskToggle")}
|
||||
active={stepMaskOn[i] ?? false}
|
||||
onclick={() => ontogglestepmask?.(i)}
|
||||
onclick={() => steps.ontogglestepmask?.(i)}
|
||||
/>
|
||||
{/if}
|
||||
{/snippet}
|
||||
@@ -197,21 +138,7 @@
|
||||
</PreviewTile>
|
||||
{/if}
|
||||
{/each}
|
||||
<SchemaResultTile
|
||||
{resultKind}
|
||||
{result}
|
||||
{resultNote}
|
||||
{fileResult}
|
||||
{textResult}
|
||||
{textVars}
|
||||
{toolId}
|
||||
{running}
|
||||
{mask}
|
||||
{maskOn}
|
||||
{ontogglemask}
|
||||
oncopy={oncopytext}
|
||||
{ondownloadtxt}
|
||||
/>
|
||||
<SchemaResultTile {resultKind} {toolId} {running} {...result} />
|
||||
</div>
|
||||
|
||||
<style>
|
||||
|
||||
@@ -500,48 +500,54 @@
|
||||
<section class="panel">
|
||||
<SchemaPreview
|
||||
toolId={lastTool.id}
|
||||
{source}
|
||||
sources={sourceFiles}
|
||||
fileCount={sourceFiles.length}
|
||||
{result}
|
||||
{resultNote}
|
||||
{fileResult}
|
||||
{textSource}
|
||||
{textResult}
|
||||
textVars={verdictVars}
|
||||
{inputMode}
|
||||
resultKind={displayResultKind}
|
||||
{running}
|
||||
error={errorText}
|
||||
{format}
|
||||
quality={currentQuality}
|
||||
{limitKb}
|
||||
{alphaLoss}
|
||||
{stepResults}
|
||||
{stepFileSets}
|
||||
{aligned}
|
||||
{stepMaskable}
|
||||
{stepMaskOn}
|
||||
{stepMasks}
|
||||
ontogglestepmask={toggleStepMaskByIndex}
|
||||
mask={stepMasks.at(-1) ?? null}
|
||||
maskOn={stepMaskOn.at(-1) ?? false}
|
||||
ontogglemask={stepMaskable.at(-1)
|
||||
? () => toggleStepMask(steps[steps.length - 1].key)
|
||||
: undefined}
|
||||
ontogglealign={() => (aligned = !aligned)}
|
||||
onformat={(v) => (format = v)}
|
||||
onquality={setFormatQuality}
|
||||
onlimit={(v) => (limitKb = v)}
|
||||
onupload={handleFile}
|
||||
onuploadmany={handleFiles}
|
||||
ontextsource={(textValue) => {
|
||||
textSource = textValue;
|
||||
head={{
|
||||
format,
|
||||
quality: currentQuality,
|
||||
limitKb,
|
||||
alphaLoss,
|
||||
onupload: handleFile,
|
||||
ondownload: download,
|
||||
onformat: (v) => (format = v),
|
||||
onquality: setFormatQuality,
|
||||
onlimit: (v) => (limitKb = v),
|
||||
}}
|
||||
source={{
|
||||
source,
|
||||
sources: sourceFiles,
|
||||
fileCount: sourceFiles.length,
|
||||
textSource,
|
||||
ontextinput: (v) => (textSource = v),
|
||||
onrendertext: run,
|
||||
onuploadmany: handleFiles,
|
||||
}}
|
||||
steps={{
|
||||
results: stepResults,
|
||||
fileSets: stepFileSets,
|
||||
maskable: stepMaskable,
|
||||
maskOn: stepMaskOn,
|
||||
masks: stepMasks,
|
||||
ontogglestepmask: toggleStepMaskByIndex,
|
||||
}}
|
||||
result={{
|
||||
result,
|
||||
resultNote,
|
||||
fileResult,
|
||||
textResult,
|
||||
textVars: verdictVars,
|
||||
mask: stepMasks.at(-1) ?? null,
|
||||
maskOn: stepMaskOn.at(-1) ?? false,
|
||||
ontogglemask: stepMaskable.at(-1)
|
||||
? () => toggleStepMask(steps[steps.length - 1].key)
|
||||
: undefined,
|
||||
oncopy: copyText,
|
||||
ondownloadtxt: downloadText,
|
||||
}}
|
||||
onrendertext={run}
|
||||
oncopytext={copyText}
|
||||
ondownloadtxt={downloadText}
|
||||
ondownload={download}
|
||||
/>
|
||||
</section>
|
||||
</div>
|
||||
|
||||
@@ -1,5 +1,12 @@
|
||||
import type { OutputMime } from "$lib/core/io";
|
||||
import type { PixelImage } from "$lib/core/types";
|
||||
import type { FileResult, InputMode, ResultKind } from "$lib/registry";
|
||||
import type {
|
||||
FileResult,
|
||||
InputMode,
|
||||
ResultKind,
|
||||
ResultNote,
|
||||
ToolImageFile,
|
||||
} from "$lib/registry";
|
||||
|
||||
export type SchemaPreviewModelInput = {
|
||||
inputMode: InputMode;
|
||||
@@ -18,3 +25,54 @@ export function hasPreviewResult(input: SchemaPreviewModelInput): boolean {
|
||||
}
|
||||
return Boolean(textResult);
|
||||
}
|
||||
|
||||
// Grouped view-models of SchemaPreview: one object per inner consumer
|
||||
// (field names mirror the consumer's own props, so they spread directly).
|
||||
|
||||
/** SchemaActions: download policy and format settings. */
|
||||
export interface PreviewHeadModel {
|
||||
format: OutputMime;
|
||||
quality?: number;
|
||||
limitKb?: number;
|
||||
alphaLoss?: boolean;
|
||||
onupload: (file: File) => void;
|
||||
ondownload: () => void;
|
||||
onformat?: (mime: OutputMime) => void;
|
||||
onquality?: (value: number) => void;
|
||||
onlimit?: (kb: number | undefined) => void;
|
||||
}
|
||||
|
||||
/** SchemaSourceTile minus mode/running/onupload (top-level/head props). */
|
||||
export interface PreviewSourceModel {
|
||||
source: PixelImage | null;
|
||||
sources?: ToolImageFile[];
|
||||
fileCount?: number;
|
||||
textSource?: string;
|
||||
ontextinput?: (text: string) => void;
|
||||
onrendertext?: () => void;
|
||||
onuploadmany?: (files: File[]) => void;
|
||||
}
|
||||
|
||||
/** Intermediate step tiles (chain mode). */
|
||||
export interface PreviewStepsModel {
|
||||
results?: (PixelImage | null)[];
|
||||
fileSets?: ToolImageFile[][];
|
||||
maskable?: boolean[];
|
||||
maskOn?: boolean[];
|
||||
masks?: (PixelImage | null)[];
|
||||
ontogglestepmask?: (i: number) => void;
|
||||
}
|
||||
|
||||
/** SchemaResultTile minus resultKind/toolId/running (top-level props). */
|
||||
export interface PreviewResultModel {
|
||||
result: PixelImage | null;
|
||||
resultNote?: ResultNote | null;
|
||||
fileResult?: FileResult | null;
|
||||
textResult?: string | null;
|
||||
textVars?: Record<string, string | number>;
|
||||
mask?: PixelImage | null;
|
||||
maskOn?: boolean;
|
||||
ontogglemask?: () => void;
|
||||
oncopy?: () => void;
|
||||
ondownloadtxt?: () => void;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user