refactor: icon and download button use button component

This commit is contained in:
2026-09-02 18:37:43 +05:00
parent 3d0f8ebb10
commit 8a6f87938d
16 changed files with 85 additions and 167 deletions
+1 -1
View File
@@ -1,6 +1,6 @@
<script lang="ts"> <script lang="ts">
import { Copy } from "@lucide/svelte"; import { Copy } from "@lucide/svelte";
import IconButton from "./IconButton.svelte"; import IconButton from "./ui/IconButton.svelte";
interface Props { interface Props {
code: string; code: string;
+1 -1
View File
@@ -1,6 +1,6 @@
<script lang="ts"> <script lang="ts">
import type { Component, Snippet } from "svelte"; import type { Component, Snippet } from "svelte";
import Icon from "./Icon.svelte"; import Icon from "./ui/Icon.svelte";
interface Props { interface Props {
title: string; title: string;
@@ -1,87 +0,0 @@
<script lang="ts">
import type { Component } from "svelte";
import Icon from "./Icon.svelte";
interface Props {
icon: Component<{ size?: number; class?: string }>;
label: string;
onclick?: () => void;
variant?: "ghost" | "solid" | "accent" | "bare";
size?: number;
disabled?: boolean;
}
let {
icon,
label,
onclick,
variant = "ghost",
size = 16,
disabled = false,
}: Props = $props();
</script>
<button
type="button"
class="icon-btn icon-btn--{variant}"
aria-label={label}
{disabled}
onclick={() => onclick?.()}
>
<Icon {icon} {size} />
</button>
<style>
.icon-btn {
display: grid;
align-items: center;
border: 1px solid var(--line);
border-radius: var(--radius);
background: transparent;
color: var(--muted);
cursor: pointer;
font-size: inherit;
line-height: inherit;
border: 0;
background: 0 0;
border: 0;
place-items: center;
padding: 6px;
display: grid;
}
.icon-btn:hover:not(:disabled) {
color: var(--foreground);
border-color: var(--blue);
}
.icon-btn--solid {
background: var(--blue);
color: #fff;
border-color: var(--blue);
}
.icon-btn--accent {
background: var(--cyan);
color: #042;
border-color: var(--cyan);
}
.icon-btn--bare {
padding: 6px;
border: 0;
border-radius: 0;
color: var(--muted);
}
.icon-btn--bare:hover:not(:disabled) {
color: var(--foreground);
border-color: transparent;
}
.icon-btn:disabled {
opacity: 0.5;
cursor: not-allowed;
}
.icon-btn--ghost {
padding: 6px;
border: 0;
border-radius: 0;
color: var(--muted);
}
</style>
+1 -12
View File
@@ -2,7 +2,7 @@
import { resolve } from "$app/paths"; import { resolve } from "$app/paths";
import { ArrowUpRight } from "@lucide/svelte"; import { ArrowUpRight } from "@lucide/svelte";
import type { Component } from "svelte"; import type { Component } from "svelte";
import Icon from "./Icon.svelte"; import Icon from "./ui/Icon.svelte";
interface Props { interface Props {
title: string; title: string;
@@ -73,15 +73,4 @@
color: var(--muted); color: var(--muted);
align-self: start; align-self: start;
} }
.tool-arrow {
color: var(--blue);
opacity: 0;
transition:
opacity 0.12s ease,
transform 0.12s ease;
}
.tool-card:hover .tool-arrow {
opacity: 1;
transform: translate(2px, -2px);
}
</style> </style>
+3 -3
View File
@@ -1,8 +1,8 @@
<script lang="ts"> <script lang="ts">
import { resolve } from "$app/paths"; import { resolve } from "$app/paths";
import { CircleQuestionMark, Moon, Sun } from "@lucide/svelte"; import { CircleQuestionMark, Moon, Sun } from "@lucide/svelte";
import IconButton from "./IconButton.svelte";
import StatusDot from "./StatusDot.svelte"; import StatusDot from "./StatusDot.svelte";
import IconButton from "./ui/IconButton.svelte";
interface Props { interface Props {
theme: "light" | "dark"; theme: "light" | "dark";
@@ -35,13 +35,13 @@
<IconButton <IconButton
icon={CircleQuestionMark} icon={CircleQuestionMark}
label="Help" label="Help"
variant="bare" variant="clear"
onclick={() => {}} onclick={() => {}}
/> />
<IconButton <IconButton
icon={theme === "light" ? Moon : Sun} icon={theme === "light" ? Moon : Sun}
label="Toggle theme" label="Toggle theme"
variant="bare" variant="clear"
onclick={ontoggle} onclick={ontoggle}
/> />
<div class="lang" role="group" aria-label="Language"> <div class="lang" role="group" aria-label="Language">
+10
View File
@@ -0,0 +1,10 @@
export const ButtonVariantDefine = {
PRIMARY: "primary",
OUTLINE: "outline",
ACCENT: "accent",
DANGER: "danger",
CLEAR: "clear",
} as const;
export type ButtonVariant =
(typeof ButtonVariantDefine)[keyof typeof ButtonVariantDefine];
@@ -1,6 +1,7 @@
<script lang="ts"> <script lang="ts">
import { Check } from "@lucide/svelte"; import { Check } from "@lucide/svelte";
// TODO: сделать эти типы общими для кнопок \ бейджей \ етц???
const ToneVariantDefine = { const ToneVariantDefine = {
DEFAULT: "default", DEFAULT: "default",
ACCENT: "accent", ACCENT: "accent",
+14 -16
View File
@@ -1,26 +1,19 @@
<script lang="ts"> <script lang="ts">
import type { Component, Snippet } from "svelte"; import type { Component } from "svelte";
import Icon from "../Icon.svelte"; import { ButtonVariantDefine, type ButtonVariant } from "../define";
import Icon from "./Icon.svelte";
const ButtonVariantDefine = {
PRIMARY: "primary",
OUTLINE: "outline",
ACCENT: "accent",
DANGER: "danger",
} as const;
type ButtonVariant =
(typeof ButtonVariantDefine)[keyof typeof ButtonVariantDefine];
interface Props { interface Props {
children: Snippet; label: string;
ariaLabel?: string;
onclick?: () => void; onclick?: () => void;
variant?: ButtonVariant; variant?: ButtonVariant;
icon?: Component<{ size?: number; class?: string }>; icon?: Component<{ size?: number; class?: string }>;
disabled?: boolean; disabled?: boolean;
} }
let { let {
children, label,
ariaLabel = label,
onclick, onclick,
variant = ButtonVariantDefine.PRIMARY, variant = ButtonVariantDefine.PRIMARY,
icon, icon,
@@ -28,9 +21,14 @@
}: Props = $props(); }: Props = $props();
</script> </script>
<button class="btn btn--{variant}" {disabled} onclick={() => onclick?.()}> <button
class="btn btn--{variant}"
{disabled}
aria-label={ariaLabel}
onclick={() => onclick?.()}
>
{#if icon}<Icon {icon} size={14} />{/if} {#if icon}<Icon {icon} size={14} />{/if}
<span class="btn-label">{@render children()}</span> <span class="btn-label">{label}</span>
</button> </button>
<style> <style>
@@ -1,5 +1,6 @@
<script lang="ts"> <script lang="ts">
import { ArrowDownToLine, Download } from "@lucide/svelte"; import { Download } from "@lucide/svelte";
import Button from "./Button.svelte";
interface Props { interface Props {
label: string; label: string;
@@ -8,29 +9,4 @@
let { label, onclick }: Props = $props(); let { label, onclick }: Props = $props();
</script> </script>
<button class="download-btn" type="button" onclick={() => onclick?.()}> <Button icon={Download} {label} {onclick} />
<Download size={16} />
{label}
<ArrowDownToLine size={14} />
</button>
<style>
.download-btn {
background: var(--color-main);
color: var(--color-background);
width: 100%;
height: calc(5 * var(--space-m));
font-size: var(--font-button);
font-family: var(--font-mono);
border: 0;
justify-content: center;
align-items: center;
gap: var(--space-m);
margin-top: var(--space-xl);
display: flex;
white-space: nowrap;
}
.download-btn:hover {
background: var(--color-main-tint);
}
</style>
@@ -0,0 +1,23 @@
<script lang="ts">
import type { Component } from "svelte";
import { ButtonVariantDefine, type ButtonVariant } from "../define";
import Button from "./Button.svelte";
interface Props {
icon: Component<{ size?: number; class?: string }>;
label: string;
onclick?: () => void;
variant?: ButtonVariant;
disabled?: boolean;
}
let {
icon,
label,
onclick,
variant = ButtonVariantDefine.PRIMARY,
disabled = false,
}: Props = $props();
</script>
<Button label="" {onclick} {variant} {disabled} {icon} ariaLabel={label} />
+7 -2
View File
@@ -38,7 +38,12 @@
placeholder="Search tools…" placeholder="Search tools…"
/> />
</div> </div>
<Button icon={Search} variant="primary" onclick={() => {}}>Search</Button> <Button
icon={Search}
variant="primary"
onclick={() => {}}
label="Search"
/>
</div> </div>
<div class="ws-results"> <div class="ws-results">
{#each results as tool (tool.id)} {#each results as tool (tool.id)}
@@ -50,7 +55,7 @@
{/each} {/each}
</div> </div>
<SettingsFooter> <SettingsFooter>
<Button onclick={() => {}}>Open last project</Button> <Button onclick={() => {}} label="Open last project" />
</SettingsFooter> </SettingsFooter>
</Panel> </Panel>
</section> </section>
+5 -5
View File
@@ -1,7 +1,6 @@
<script lang="ts"> <script lang="ts">
import ColorField from "$lib/components/kit/ColorField.svelte"; import ColorField from "$lib/components/kit/ColorField.svelte";
import FieldGrid from "$lib/components/kit/FieldGrid.svelte"; import FieldGrid from "$lib/components/kit/FieldGrid.svelte";
import IconButton from "$lib/components/kit/IconButton.svelte";
import PageGrid from "$lib/components/kit/layout/PageGrid.svelte"; import PageGrid from "$lib/components/kit/layout/PageGrid.svelte";
import MetaList from "$lib/components/kit/MetaList.svelte"; import MetaList from "$lib/components/kit/MetaList.svelte";
import PipelineFooter from "$lib/components/kit/PipelineFooter.svelte"; import PipelineFooter from "$lib/components/kit/PipelineFooter.svelte";
@@ -14,6 +13,7 @@
import ToggleRow from "$lib/components/kit/ToggleRow.svelte"; import ToggleRow from "$lib/components/kit/ToggleRow.svelte";
import Badge from "$lib/components/kit/ui/Badge.svelte"; import Badge from "$lib/components/kit/ui/Badge.svelte";
import DownloadButton from "$lib/components/kit/ui/DownloadButton.svelte"; import DownloadButton from "$lib/components/kit/ui/DownloadButton.svelte";
import IconButton from "$lib/components/kit/ui/IconButton.svelte";
import { import {
ChevronDown, ChevronDown,
Plus, Plus,
@@ -95,7 +95,7 @@
onremove={() => {}} onremove={() => {}}
> >
{#snippet tools()} {#snippet tools()}
<Badge tone="success" check>AUTO</Badge> <Badge tone="success" check label="AUTO" />
{/snippet} {/snippet}
<FieldGrid> <FieldGrid>
<ColorField label="COLOR" chevron bind:value={gradColor} /> <ColorField label="COLOR" chevron bind:value={gradColor} />
@@ -128,7 +128,7 @@
onremove={() => {}} onremove={() => {}}
> >
{#snippet tools()} {#snippet tools()}
<Badge tone="success" check>AUTO</Badge> <Badge tone="success" check label="AUTO" />
{/snippet} {/snippet}
<div class="transform-note"> <div class="transform-note">
<SlidersHorizontal size={15} /> Automatic subject detection enabled <SlidersHorizontal size={15} /> Automatic subject detection enabled
@@ -137,7 +137,7 @@
<StepCard index={3} type="STYLE" title="Add outline" onremove={() => {}}> <StepCard index={3} type="STYLE" title="Add outline" onremove={() => {}}>
{#snippet tools()} {#snippet tools()}
<Badge tone="success" check>AUTO</Badge> <Badge tone="success" check label="AUTO" />
{/snippet} {/snippet}
<FieldGrid compact> <FieldGrid compact>
<SliderField <SliderField
@@ -158,7 +158,7 @@
onremove={() => {}} onremove={() => {}}
> >
{#snippet tools()} {#snippet tools()}
<Badge tone="success" check>AUTO</Badge> <Badge tone="success" check label="AUTO" />
{/snippet} {/snippet}
<FieldGrid compact> <FieldGrid compact>
<SliderField <SliderField
+8 -8
View File
@@ -4,7 +4,6 @@
import CodeBlock from "$lib/components/kit/CodeBlock.svelte"; import CodeBlock from "$lib/components/kit/CodeBlock.svelte";
import ColorField from "$lib/components/kit/ColorField.svelte"; import ColorField from "$lib/components/kit/ColorField.svelte";
import EmptyState from "$lib/components/kit/EmptyState.svelte"; import EmptyState from "$lib/components/kit/EmptyState.svelte";
import IconButton from "$lib/components/kit/IconButton.svelte";
import ImageCard from "$lib/components/kit/ImageCard.svelte"; import ImageCard from "$lib/components/kit/ImageCard.svelte";
import MetaList from "$lib/components/kit/MetaList.svelte"; import MetaList from "$lib/components/kit/MetaList.svelte";
import Panel from "$lib/components/kit/Panel.svelte"; import Panel from "$lib/components/kit/Panel.svelte";
@@ -23,6 +22,7 @@
import Badge from "$lib/components/kit/ui/Badge.svelte"; import Badge from "$lib/components/kit/ui/Badge.svelte";
import Button from "$lib/components/kit/ui/Button.svelte"; import Button from "$lib/components/kit/ui/Button.svelte";
import DownloadButton from "$lib/components/kit/ui/DownloadButton.svelte"; import DownloadButton from "$lib/components/kit/ui/DownloadButton.svelte";
import IconButton from "$lib/components/kit/ui/IconButton.svelte";
import { Download, ImageOff, Palette, Plus, Trash2 } from "@lucide/svelte"; import { Download, ImageOff, Palette, Plus, Trash2 } from "@lucide/svelte";
let mode = $state("preview"); let mode = $state("preview");
@@ -62,18 +62,18 @@
<PanelHeading title="Buttons" eyebrow="controls" /> <PanelHeading title="Buttons" eyebrow="controls" />
<div class="stack"> <div class="stack">
<SettingGroup label="variants"> <SettingGroup label="variants">
<Button onclick={() => {}}>Primary</Button> <Button onclick={() => {}} label="Primary" />
<Button variant="outline" onclick={() => {}}>Outline</Button> <Button variant="outline" onclick={() => {}} label="Outline" />
<Button variant="accent" onclick={() => {}}>Accent</Button> <Button variant="accent" onclick={() => {}} label="Accent" />
<Button variant="danger" onclick={() => {}}>Danger</Button> <Button variant="danger" onclick={() => {}} label="Danger" />
</SettingGroup> </SettingGroup>
<SettingGroup label="states"> <SettingGroup label="states">
<Button disabled onclick={() => {}}>Disabled</Button> <Button disabled onclick={() => {}} label="Disabled" />
<IconButton icon={Plus} label="Add" onclick={() => {}} /> <IconButton icon={Plus} label="Add" onclick={() => {}} />
<IconButton <IconButton
icon={Trash2} icon={Trash2}
label="Delete" label="Delete"
variant="solid" variant="primary"
onclick={() => {}} onclick={() => {}}
/> />
<IconButton <IconButton
@@ -202,7 +202,7 @@
title="No image loaded" title="No image loaded"
description="Drop a PNG to start processing" description="Drop a PNG to start processing"
> >
<Button onclick={() => {}}>Open file</Button> <Button onclick={() => {}} label="Open file" />
</EmptyState> </EmptyState>
<CodeBlock <CodeBlock
code={'export const config = {\n radius: 8,\n tint: "#3b82f6",\n};'} code={'export const config = {\n radius: 8,\n tint: "#3b82f6",\n};'}
@@ -67,18 +67,21 @@
{#if needsSource && !sourceImg} {#if needsSource && !sourceImg}
<Dropzone onfile={onFile} /> <Dropzone onfile={onFile} />
{:else if needsSource && sourceImg} {:else if needsSource && sourceImg}
<Button variant="ghost" onclick={onClearSource}>Change image</Button> <Button variant="outline" onclick={onClearSource} label="Change image" />
{/if} {/if}
<PreviewStack> <PreviewStack>
{#if needsSource && sourceImg} {#if needsSource && sourceImg}
<PreviewTile label="SOURCE"> <PreviewTile label="SOURCE" caption="">
<CheckerCanvas size="sm"> <CheckerCanvas size="sm">
<img class="tile-img" src={sourceUrl} alt="source" /> <img class="tile-img" src={sourceUrl} alt="source" />
</CheckerCanvas> </CheckerCanvas>
</PreviewTile> </PreviewTile>
{/if} {/if}
{#each stepOutputs as out, i (out.toolId + i)} {#each stepOutputs as out, i (out.toolId + i)}
<PreviewTile label={`STEP ${String(i + 1).padStart(2, "0")}`}> <PreviewTile
label={`STEP ${String(i + 1).padStart(2, "0")}`}
caption=""
>
<CheckerCanvas size="sm"> <CheckerCanvas size="sm">
<img class="tile-img" src={out.url} alt={out.title} /> <img class="tile-img" src={out.url} alt={out.title} />
</CheckerCanvas> </CheckerCanvas>
@@ -41,7 +41,7 @@
<Panel> <Panel>
<PanelHeading title="Pipeline" eyebrow="STEPS"> <PanelHeading title="Pipeline" eyebrow="STEPS">
{#snippet actions()} {#snippet actions()}
<Badge tone="accent">AUTO</Badge> <Badge tone="accent" label="AUTO" />
{/snippet} {/snippet}
</PanelHeading> </PanelHeading>
<div class="pipeline-body"> <div class="pipeline-body">
@@ -78,7 +78,7 @@
options={chainOptions} options={chainOptions}
onchange={(v) => onSetAddId(v)} onchange={(v) => onSetAddId(v)}
/> />
<Button variant="ghost" onclick={onAddStep}>Add step</Button> <Button variant="outline" onclick={onAddStep} label="Add step" />
</div> </div>
</SettingsFooter> </SettingsFooter>
</Panel> </Panel>