mirror of
https://github.com/Ku6epXBOCTuK/easy-png-tools.git
synced 2026-09-14 21:46:35 +00:00
refactor: move form fields to separate folder
This commit is contained in:
@@ -0,0 +1,32 @@
|
||||
<script lang="ts">
|
||||
interface Props {
|
||||
label: string;
|
||||
checked: boolean;
|
||||
onchange?: (checked: boolean) => void;
|
||||
}
|
||||
let { label, checked = $bindable(), onchange }: Props = $props();
|
||||
|
||||
function handle(e: Event) {
|
||||
checked = (e.target as HTMLInputElement).checked;
|
||||
onchange?.(checked);
|
||||
}
|
||||
</script>
|
||||
|
||||
<label class="checkbox-field">
|
||||
<input type="checkbox" {checked} onchange={handle} />
|
||||
<span class="checkbox-label">{label}</span>
|
||||
</label>
|
||||
|
||||
<style>
|
||||
.checkbox-field {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
font-size: 0.85rem;
|
||||
color: var(--foreground);
|
||||
cursor: pointer;
|
||||
}
|
||||
.checkbox-field input {
|
||||
accent-color: var(--blue);
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,80 @@
|
||||
<script lang="ts">
|
||||
import { ChevronDown } from "@lucide/svelte";
|
||||
|
||||
interface Props {
|
||||
label: string;
|
||||
value: string;
|
||||
dark?: boolean;
|
||||
chevron?: boolean;
|
||||
oninput?: (value: string) => void;
|
||||
}
|
||||
let {
|
||||
label,
|
||||
value = $bindable(),
|
||||
dark = false,
|
||||
chevron = false,
|
||||
oninput,
|
||||
}: Props = $props();
|
||||
|
||||
function handle(e: Event) {
|
||||
value = (e.target as HTMLInputElement).value;
|
||||
oninput?.(value);
|
||||
}
|
||||
</script>
|
||||
|
||||
<div class="control-block">
|
||||
<label>{label}</label>
|
||||
<div class="color-field">
|
||||
<span class="swatch" class:dark style="background:{value}"></span>
|
||||
<input
|
||||
type="text"
|
||||
aria-label={label}
|
||||
{value}
|
||||
oninput={handle}
|
||||
spellcheck="false"
|
||||
/>
|
||||
{#if chevron}<ChevronDown class="icon" size={14} />{/if}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<style>
|
||||
.control-block :global(.icon) {
|
||||
color: var(--foreground);
|
||||
}
|
||||
label {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
font: 10px var(--font-mono);
|
||||
letter-spacing: 0.08em;
|
||||
color: var(--muted);
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
.color-field {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
padding-left: 8px;
|
||||
padding-right: 8px;
|
||||
height: 32px;
|
||||
border: 1px solid var(--line);
|
||||
}
|
||||
.swatch {
|
||||
width: 14px;
|
||||
height: 14px;
|
||||
border: 1px solid var(--line);
|
||||
flex: none;
|
||||
}
|
||||
.color-field input {
|
||||
min-width: 0;
|
||||
color: var(--foreground);
|
||||
font: 11px var(--font-mono);
|
||||
background: 0 0;
|
||||
border: 0;
|
||||
outline: 0;
|
||||
flex: 1;
|
||||
}
|
||||
.color-field :global(svg) {
|
||||
color: var(--muted);
|
||||
flex: none;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,35 @@
|
||||
<script lang="ts">
|
||||
import type { Snippet } from "svelte";
|
||||
import MonoLabel from "../MonoLabel.svelte";
|
||||
|
||||
interface Props {
|
||||
label: string;
|
||||
children: Snippet;
|
||||
hint?: string;
|
||||
}
|
||||
let { label, children, hint }: Props = $props();
|
||||
</script>
|
||||
|
||||
<div class="field">
|
||||
<MonoLabel>{label}</MonoLabel>
|
||||
<div class="field-control">{@render children()}</div>
|
||||
{#if hint}<span class="field-hint">{hint}</span>{/if}
|
||||
</div>
|
||||
|
||||
<style>
|
||||
.field {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: var(--space-s);
|
||||
}
|
||||
.field-control {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: var(--space-m);
|
||||
}
|
||||
.field-hint {
|
||||
font-family: var(--font-mono);
|
||||
font-size: var(--font-size-s);
|
||||
color: var(--color-text-muted);
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,35 @@
|
||||
<script lang="ts">
|
||||
import type { Snippet } from "svelte";
|
||||
|
||||
interface Props {
|
||||
compact?: boolean;
|
||||
children: Snippet;
|
||||
}
|
||||
|
||||
let { compact = false, children }: Props = $props();
|
||||
</script>
|
||||
|
||||
<div class="controls" class:compact>
|
||||
{@render children()}
|
||||
</div>
|
||||
|
||||
<style>
|
||||
.controls {
|
||||
display: grid;
|
||||
grid-template-columns: 1.1fr 1fr 1.2fr;
|
||||
gap: 16px;
|
||||
}
|
||||
.controls.compact {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
gap: 16px;
|
||||
}
|
||||
|
||||
@media (max-width: 800px) {
|
||||
.controls,
|
||||
.controls.compact {
|
||||
grid-auto-flow: row;
|
||||
grid-auto-columns: auto;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,47 @@
|
||||
<script lang="ts">
|
||||
import Field from "./Field.svelte";
|
||||
|
||||
interface Props {
|
||||
label: string;
|
||||
value: number;
|
||||
min?: number;
|
||||
max?: number;
|
||||
step?: number;
|
||||
oninput?: (value: number) => void;
|
||||
}
|
||||
let { label, value = $bindable(), min, max, step, oninput }: Props = $props();
|
||||
|
||||
function handle(e: Event) {
|
||||
value = Number((e.target as HTMLInputElement).value);
|
||||
oninput?.(value);
|
||||
}
|
||||
</script>
|
||||
|
||||
<Field {label}>
|
||||
<input
|
||||
class="number-field"
|
||||
type="number"
|
||||
{min}
|
||||
{max}
|
||||
{step}
|
||||
{value}
|
||||
oninput={handle}
|
||||
/>
|
||||
</Field>
|
||||
|
||||
<style>
|
||||
.number-field {
|
||||
width: 100%;
|
||||
font-family: var(--font-mono);
|
||||
font-size: 11px;
|
||||
color: var(--foreground);
|
||||
background: var(--background);
|
||||
border: 1px solid var(--line);
|
||||
border-radius: var(--radius);
|
||||
padding: 0.35rem 0.5rem;
|
||||
}
|
||||
.number-field:focus {
|
||||
outline: none;
|
||||
border-color: var(--blue);
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,42 @@
|
||||
<script lang="ts">
|
||||
import Field from "./Field.svelte";
|
||||
|
||||
interface Option {
|
||||
value: string;
|
||||
label: string;
|
||||
}
|
||||
|
||||
interface Props {
|
||||
label: string;
|
||||
value: string;
|
||||
options: Option[];
|
||||
onchange?: (value: string) => void;
|
||||
}
|
||||
let { label, value = $bindable(), options, onchange }: Props = $props();
|
||||
|
||||
function handle(e: Event) {
|
||||
value = (e.target as HTMLSelectElement).value;
|
||||
onchange?.(value);
|
||||
}
|
||||
</script>
|
||||
|
||||
<Field {label}>
|
||||
<select class="select-field" {value} onchange={handle}>
|
||||
{#each options as opt (opt.value)}
|
||||
<option value={opt.value}>{opt.label}</option>
|
||||
{/each}
|
||||
</select>
|
||||
</Field>
|
||||
|
||||
<style>
|
||||
.select-field {
|
||||
width: 100%;
|
||||
font-family: var(--font-mono);
|
||||
font-size: 11px;
|
||||
color: var(--foreground);
|
||||
background: var(--background);
|
||||
border: 1px solid var(--line);
|
||||
border-radius: var(--radius);
|
||||
padding: 0.35rem 0.5rem;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,51 @@
|
||||
<script lang="ts">
|
||||
interface Props {
|
||||
label: string;
|
||||
value: number;
|
||||
min?: number;
|
||||
max?: number;
|
||||
step?: number;
|
||||
suffix?: string;
|
||||
oninput?: (value: number) => void;
|
||||
}
|
||||
let {
|
||||
label,
|
||||
value = $bindable(),
|
||||
min = 0,
|
||||
max = 100,
|
||||
step = 1,
|
||||
suffix = "",
|
||||
oninput,
|
||||
}: Props = $props();
|
||||
|
||||
function handle(e: Event) {
|
||||
value = Number((e.target as HTMLInputElement).value);
|
||||
oninput?.(value);
|
||||
}
|
||||
</script>
|
||||
|
||||
<div class="control-block">
|
||||
<label>{label} <output>{value}{suffix}</output></label>
|
||||
<input type="range" {min} {max} {step} {value} oninput={handle} />
|
||||
</div>
|
||||
|
||||
<style>
|
||||
label {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
font: 10px var(--font-mono);
|
||||
letter-spacing: 0.08em;
|
||||
color: var(--muted);
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
output {
|
||||
font: 10px var(--font-mono);
|
||||
color: var(--foreground);
|
||||
}
|
||||
input[type="range"] {
|
||||
color: var(--foreground);
|
||||
line-height: inherit;
|
||||
width: 100%;
|
||||
accent-color: var(--blue);
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,50 @@
|
||||
<script lang="ts">
|
||||
interface Props {
|
||||
value: string;
|
||||
type?: string;
|
||||
placeholder?: string;
|
||||
readonly?: boolean;
|
||||
label?: string;
|
||||
oninput?: (value: string) => void;
|
||||
}
|
||||
let {
|
||||
value = $bindable(),
|
||||
type = "text",
|
||||
placeholder = "",
|
||||
readonly = false,
|
||||
label,
|
||||
oninput,
|
||||
}: Props = $props();
|
||||
|
||||
function handle(e: Event) {
|
||||
value = (e.target as HTMLInputElement).value;
|
||||
oninput?.(value);
|
||||
}
|
||||
</script>
|
||||
|
||||
<input
|
||||
class="text-field"
|
||||
{type}
|
||||
{placeholder}
|
||||
{readonly}
|
||||
{value}
|
||||
aria-label={label}
|
||||
oninput={handle}
|
||||
/>
|
||||
|
||||
<style>
|
||||
.text-field {
|
||||
width: 100%;
|
||||
font-family: var(--font-mono);
|
||||
font-size: 11px;
|
||||
color: var(--foreground);
|
||||
background: var(--background);
|
||||
border: 1px solid var(--line);
|
||||
border-radius: var(--radius);
|
||||
padding: 0.35rem 0.5rem;
|
||||
}
|
||||
.text-field:focus {
|
||||
outline: none;
|
||||
border-color: var(--blue);
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,23 @@
|
||||
<script lang="ts">
|
||||
import Toggle from "../ui/Toggle.svelte";
|
||||
|
||||
interface Props {
|
||||
label: string;
|
||||
checked: boolean;
|
||||
onchange?: (checked: boolean) => void;
|
||||
}
|
||||
|
||||
let { label, checked = $bindable(), onchange }: Props = $props();
|
||||
</script>
|
||||
|
||||
<div class="toggle-row">
|
||||
<span>{label}</span>
|
||||
<Toggle bind:checked {onchange} {label} />
|
||||
</div>
|
||||
|
||||
<style>
|
||||
.toggle-row {
|
||||
color: var(--foreground);
|
||||
cursor: pointer;
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user