From 3aa06d4dc4956319bfcc58c0bb39359172c71bba Mon Sep 17 00:00:00 2001 From: simoleo89 Date: Sun, 24 May 2026 21:47:39 +0200 Subject: [PATCH] feat(floorplan-editor): height slider thumb adopts the colour of the band under it Feedback was the amber thumb looked generic / off-the-shelf and didnt visually tie to the gradient. The thumb now picks its fill from tileFill of the selected height, so picking 0 shows a blue bead, picking 12 a green one, picking 26 a purple one, and so on across the full HEIGHT_SCHEME palette. - Fill: radial gradient on the band colour with a soft white highlight at top-left and a darker rim at the bottom-right for a beaded look. The highlight intensity adapts to the base colour (stronger on dark hues, dimmer on light) so it never washes out. - Text contrast: a perceptual-luma heuristic (Rec.601, plain arithmetic, no colour lib) flips between text-zinc-900 and text-white at the right threshold so the height number stays legible on every colour the picker can land on. A matching textShadow seals the deal on the borderline hues. - Ring on drag is now zinc-900 + scale-110 (clear gesture feedback even when the underlying colour is similar to white). - Test added: thumb fill at h=0 must differ from h=13, so any future regression that pins the thumb to a single colour fails the suite. --- .../views/FloorplanHeightPicker.test.tsx | 18 +++++++++ .../views/FloorplanHeightPicker.tsx | 39 +++++++++++++++++-- 2 files changed, 53 insertions(+), 4 deletions(-) diff --git a/src/components/floorplan-editor/views/FloorplanHeightPicker.test.tsx b/src/components/floorplan-editor/views/FloorplanHeightPicker.test.tsx index 9c047fe..f29f1fe 100644 --- a/src/components/floorplan-editor/views/FloorplanHeightPicker.test.tsx +++ b/src/components/floorplan-editor/views/FloorplanHeightPicker.test.tsx @@ -121,4 +121,22 @@ describe('FloorplanHeightPicker', () => restore(); }); + + it('thumb fill matches the tile colour at the picked height', () => + { + // h=0 is solid blue (#0065ff in COLORMAP). Re-render at a + // different height and assert the recorded thumb colour + // changes — i.e., the thumb tracks the band underneath. + const { rerender } = render( undefined } />); + + const colourAtZero = screen.getByTestId('height-thumb').getAttribute('data-thumb-color'); + + rerender( undefined } />); + + const colourAtThirteen = screen.getByTestId('height-thumb').getAttribute('data-thumb-color'); + + expect(colourAtZero).toBeTruthy(); + expect(colourAtThirteen).toBeTruthy(); + expect(colourAtZero).not.toBe(colourAtThirteen); + }); }); diff --git a/src/components/floorplan-editor/views/FloorplanHeightPicker.tsx b/src/components/floorplan-editor/views/FloorplanHeightPicker.tsx index 758dec8..b8dfd28 100644 --- a/src/components/floorplan-editor/views/FloorplanHeightPicker.tsx +++ b/src/components/floorplan-editor/views/FloorplanHeightPicker.tsx @@ -12,6 +12,26 @@ const TRACK_H = 260; const THUMB_DIAM = 28; const RAIL_GUTTER = 4; +/** + * Perceptual-luminance heuristic. Returns true if a hex colour is + * 'light enough' that black text reads better than white. Uses the + * Rec. 601 luma coefficients — good enough for a UI affordance, + * cheap to compute, no dep on a colour lib. + */ +const isLightColor = (hex: string): boolean => +{ + const c = hex.replace('#', ''); + + if(c.length !== 6) return true; + + const r = parseInt(c.slice(0, 2), 16); + const g = parseInt(c.slice(2, 4), 16); + const b = parseInt(c.slice(4, 6), 16); + const luma = (0.299 * r) + (0.587 * g) + (0.114 * b); + + return luma > 160; +}; + /** * Vertical brush-height slider. * @@ -107,6 +127,8 @@ export const FloorplanHeightPicker: FC = ({ selectedH, onSelect }) => }, [ isDragging, heightFromClientY, onSelect, selectedH ]); const thumbPct = ((HEIGHT_BRUSH_MAX - selectedH) / (count - 1)) * 100; + const thumbColor = tileFill({ h: selectedH, blocked: false }); + const thumbTextDark = isLightColor(thumbColor); return (
= ({ selectedH, onSelect }) => />
{ selectedH }