fix(studio): a slider commits the value it was moved to, and the paint render test uses a real shape

- The opacity and paint sliders remember the value they were last moved to and commit that when let go, instead of reading the controlled input, which still shows the old value until the parent renders.
- The render test's old-fade shape is star-5; star is not a shape, so the whole fixture failed to parse.
This commit is contained in:
Maurice
2026-10-23 21:07:04 +02:00
parent e7a5316990
commit 473132fc07
3 changed files with 28 additions and 9 deletions
@@ -8,7 +8,7 @@ import {
type BookPatternPaint,
} from '@trek/shared';
import { ArrowLeftRight, Ban, Trash2 } from 'lucide-react';
import { useState } from 'react';
import { useRef, useState } from 'react';
import '../../styles/studio-paint.css';
import { NumField } from './StudioControls';
import type { StudioT } from './StudioSidebar';
@@ -339,6 +339,8 @@ function Slider({
return log ? min * (max / min) ** t : min + (max - min) * t;
};
const pos = Math.round((log ? Math.log(value / min) / Math.log(max / min) : (value - min) / (max - min)) * steps);
// What the slider was last moved to: a controlled input may still show the old value when it is let go.
const moved = useRef(value);
return (
<label className="st-paint-slider">
<span className="st-paint-slider-head">
@@ -351,9 +353,18 @@ function Slider({
max={steps}
step={1}
value={pos}
onChange={(e) => onChange(toValue(e.target.value), false)}
onPointerUp={(e) => onChange(toValue(e.currentTarget.value), true)}
onKeyUp={(e) => onChange(toValue(e.currentTarget.value), true)}
onPointerDown={() => {
moved.current = value;
}}
onKeyDown={() => {
moved.current = value;
}}
onChange={(e) => {
moved.current = toValue(e.target.value);
onChange(moved.current, false);
}}
onPointerUp={() => onChange(moved.current, true)}
onKeyUp={() => onChange(moved.current, true)}
/>
</label>
);
@@ -114,7 +114,13 @@ export function AlphaSlider({ color, value, onChange, label }: {
label?: string
}) {
const pct = Math.round(value * 100)
const finish = (raw: string) => onChange(Math.max(0, Math.min(100, Number(raw))) / 100, true)
// What the slider was last moved to: a controlled input may still show the old value when it is let go.
const moved = useRef(value)
const move = (raw: string) => {
moved.current = Math.max(0, Math.min(100, Number(raw))) / 100
onChange(moved.current, false)
}
const finish = () => onChange(moved.current, true)
return (
<div className="st-alpha">
<div className="st-alpha-track" style={{ ['--st-alpha-colour' as string]: color }}>
@@ -125,9 +131,11 @@ export function AlphaSlider({ color, value, onChange, label }: {
step={1}
value={pct}
aria-label={label}
onChange={e => onChange(Number(e.target.value) / 100, false)}
onPointerUp={e => finish(e.currentTarget.value)}
onKeyUp={e => finish(e.currentTarget.value)}
onPointerDown={() => { moved.current = value }}
onKeyDown={() => { moved.current = value }}
onChange={e => move(e.target.value)}
onPointerUp={finish}
onKeyUp={finish}
/>
</div>
<span className="st-alpha-value">{pct}%</span>