From d8f7143b4ccc73cb52ae6b39c4e3f5458431f4ac Mon Sep 17 00:00:00 2001 From: Maurice Date: Fri, 23 Oct 2026 21:07:04 +0200 Subject: [PATCH] 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. --- .../components/Studio/StudioPaintPicker.tsx | 19 +++++++++++++++---- .../src/components/Studio/StudioSwatches.tsx | 16 ++++++++++++---- client/tests/unit/studio/paintRender.test.tsx | 2 +- 3 files changed, 28 insertions(+), 9 deletions(-) diff --git a/client/src/components/Studio/StudioPaintPicker.tsx b/client/src/components/Studio/StudioPaintPicker.tsx index 078c00f10..b2c785e3c 100644 --- a/client/src/components/Studio/StudioPaintPicker.tsx +++ b/client/src/components/Studio/StudioPaintPicker.tsx @@ -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 ( ); diff --git a/client/src/components/Studio/StudioSwatches.tsx b/client/src/components/Studio/StudioSwatches.tsx index 6aa05460a..0847f9534 100644 --- a/client/src/components/Studio/StudioSwatches.tsx +++ b/client/src/components/Studio/StudioSwatches.tsx @@ -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 (
@@ -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} />
{pct}% diff --git a/client/tests/unit/studio/paintRender.test.tsx b/client/tests/unit/studio/paintRender.test.tsx index f7879aa74..602716fc6 100644 --- a/client/tests/unit/studio/paintRender.test.tsx +++ b/client/tests/unit/studio/paintRender.test.tsx @@ -65,7 +65,7 @@ const painted = (): BookSpread => ({ shape('g1', { paint: linear }), shape('h1', { shape: 'heart', paint: dots, stroke: '#000000', strokeWidth: 1 }), shape('r1', { shape: 'rect', radius: 4, paint: dots }), - shape('old', { fill: '#336699', gradient: 'down', shape: 'star' }), + shape('old', { fill: '#336699', gradient: 'down', shape: 'star-5' }), bookPhotoElementSchema.parse({ id: 'p1', kind: 'photo', frame, photoId: 3, mask: 'heart' }) as BookElement, ], parked: [],