fix: update range slider field component

This commit is contained in:
2026-10-04 20:34:40 +05:00
parent 5743b9fb3f
commit e73986fefd
2 changed files with 74 additions and 9 deletions
-2
View File
@@ -9,8 +9,6 @@
Порядок согласован: C (контролы полей) → B (загрузка исходника) → E (download) → Порядок согласован: C (контролы полей) → B (загрузка исходника) → E (download) →
D (pipeline). Делать по одной, сверху вниз. D (pipeline). Делать по одной, сверху вниз.
- [ ] **C1. range slider field** — добавить поле для ввода цифр и кнопку сброса
значения
- [ ] **C2. keep Aspect field** — пересчитывать второе поле, учитывать какое - [ ] **C2. keep Aspect field** — пересчитывать второе поле, учитывать какое
было изменено было изменено
- [ ] **C3. пресеты размеров** — split-into-parts-png, resize (может еще - [ ] **C3. пресеты размеров** — split-into-parts-png, resize (может еще
@@ -1,5 +1,8 @@
<script lang="ts"> <script lang="ts">
import type { FieldSpec, NumberSpec, SliderSpec } from "$lib/registry-schema"; import type { FieldSpec, NumberSpec, SliderSpec } from "$lib/registry-schema";
import { t } from "$lib/i18n/t";
import { Minus, Plus, RotateCcw } from "@lucide/svelte";
import IconButton from "$lib/components/ui/IconButton.svelte";
import Control from "./Control.svelte"; import Control from "./Control.svelte";
interface Props { interface Props {
@@ -11,33 +14,97 @@
let { label, value, spec, onchange }: Props = $props(); let { label, value, spec, onchange }: Props = $props();
const sp = $derived(spec as NumberSpec | SliderSpec); const sp = $derived(spec as NumberSpec | SliderSpec);
const min = $derived(sp.min ?? 0);
const max = $derived(sp.max ?? 100);
const step = $derived(sp.step ?? 1);
const current = $derived( const current = $derived(
typeof value === "number" && Number.isFinite(value) ? value : sp.default, typeof value === "number" && Number.isFinite(value) ? value : sp.default,
); );
const atDefault = $derived(current === sp.default);
const atMin = $derived(current <= min);
const atMax = $derived(current >= max);
function handle(e: Event) { function handle(e: Event) {
onchange?.(Number((e.target as HTMLInputElement).value)); const n = (e.target as HTMLInputElement).valueAsNumber;
if (Number.isFinite(n)) onchange?.(n);
}
function stepBy(dir: 1 | -1) {
onchange?.(Math.min(max, Math.max(min, current + dir * step)));
} }
</script> </script>
<Control element="label" {label}> <Control {label}>
{#snippet trailing()} {#snippet trailing()}
<output>{current}</output> <span class="range-trailing">
<IconButton
icon={Minus}
label={t("ui.decrease")}
variant="clear"
size="s"
disabled={atMin}
onclick={() => stepBy(-1)}
/>
<input
class="range-number"
aria-label={label}
type="number"
{min}
{max}
{step}
value={current}
oninput={handle}
/>
<IconButton
icon={Plus}
label={t("ui.increase")}
variant="clear"
size="s"
disabled={atMax}
onclick={() => stepBy(1)}
/>
<IconButton
icon={RotateCcw}
label={t("ui.reset")}
variant="clear"
size="s"
disabled={atDefault}
onclick={() => onchange?.(sp.default)}
/>
</span>
{/snippet} {/snippet}
<input <input
aria-label={label} aria-label={label}
type="range" type="range"
min={sp.min ?? 0} {min}
max={sp.max ?? 100} {max}
step={sp.step ?? 1} {step}
value={current} value={current}
oninput={handle} oninput={handle}
/> />
</Control> </Control>
<style> <style>
output { .range-trailing {
display: inline-flex;
align-items: center;
gap: var(--space-s);
}
.range-number {
width: calc(var(--space-xxxl) * 3);
padding: var(--space-m) var(--space-l);
background: var(--color-background);
border: var(--size-border) solid var(--color-border);
border-radius: var(--radius-s);
color: var(--color-text); color: var(--color-text);
font: var(--font-size-m) var(--font-mono);
text-align: center;
appearance: textfield;
}
.range-number::-webkit-outer-spin-button,
.range-number::-webkit-inner-spin-button {
appearance: none;
margin: 0;
} }
input[type="range"] { input[type="range"] {
width: 100%; width: 100%;