Show number fields with three decimals and snap dragging to the step
Number fields derived their display precision from `step`, so `step: 1` (X/Y/Z, width, height, font size) rendered a stored 100.234 as "100" — and because `onChange` parses the text, touching the field wrote the rounded value back into the project. `formatNumber` now renders every number with three decimals, independent of `step`, which is left to control the scrub and arrow-key increment only. Dragging now snaps to the increment's grid (`snapToStep`), so a scrub at `step: 1` always lands on whole numbers even from a fractional start, while Alt (x0.1) and the 0.1-step fields keep their precision. Typed values and arrow keys are untouched. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
1 parent
15ffcd5739
commit
6168df41f8
6 files changed
+104
-21
No files matched your search
+55
-1
@@ -1,5 +1,5 @@
|
||||
import { describe, expect, it } from 'vitest';
|
||||
import { clamp, centreY, zoomFactor } from '../../src/ui/math';
|
||||
import { clamp, centreY, formatNumber, NUMBER_DECIMALS, snapToStep, zoomFactor } from '../../src/ui/math';
|
||||
|
||||
describe('clamp', () => {
|
||||
it('keeps a value inside the range', () => {
|
||||
@@ -21,6 +21,60 @@ describe('centreY', () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('formatNumber', () => {
|
||||
it('always shows three decimals', () => {
|
||||
expect(NUMBER_DECIMALS).toBe(3);
|
||||
expect(formatNumber(100)).toBe('100.000');
|
||||
expect(formatNumber(0.5)).toBe('0.500');
|
||||
expect(formatNumber(-12)).toBe('-12.000');
|
||||
});
|
||||
|
||||
it('never hides a fractional value behind a whole number', () => {
|
||||
// The bug this replaces: a field with step 1 showed a stored 100.234 as "100", and
|
||||
// committing that text wrote the rounded value back into the project.
|
||||
expect(formatNumber(100.234)).toBe('100.234');
|
||||
expect(formatNumber(0.001)).toBe('0.001');
|
||||
});
|
||||
|
||||
it('rounds to the last shown place', () => {
|
||||
expect(formatNumber(1.2345)).toBe('1.234');
|
||||
expect(formatNumber(1.2356)).toBe('1.236');
|
||||
});
|
||||
|
||||
it('drops the minus sign from a value that displays as zero', () => {
|
||||
expect(formatNumber(-0)).toBe('0.000');
|
||||
expect(formatNumber(-0.0001)).toBe('0.000');
|
||||
});
|
||||
|
||||
it('honours an explicit precision', () => {
|
||||
expect(formatNumber(1.25, 1)).toBe('1.3');
|
||||
expect(formatNumber(1.25, 0)).toBe('1');
|
||||
});
|
||||
});
|
||||
|
||||
describe('snapToStep', () => {
|
||||
it('lands on whole numbers for a step-1 field', () => {
|
||||
// The bug: a scrub added fractional device pixels to an already fractional value, so
|
||||
// dragging X produced values like 100.667 instead of 101.
|
||||
expect(snapToStep(100.667, 1)).toBe(101);
|
||||
expect(snapToStep(100.333, 1)).toBe(100);
|
||||
expect(snapToStep(-3.5, 1)).toBe(-3);
|
||||
});
|
||||
|
||||
it('uses the increment as the grid, so a fine drag keeps its precision', () => {
|
||||
// Alt halves the step to 0.1: snapping must not swallow the fine adjustment.
|
||||
expect(snapToStep(1.24, 0.1)).toBe(1.2);
|
||||
expect(snapToStep(1.26, 0.1)).toBe(1.3);
|
||||
expect(snapToStep(3.0000000000000004, 0.1)).toBe(3);
|
||||
});
|
||||
|
||||
it('passes values through when there is no grid', () => {
|
||||
expect(snapToStep(1.234, 0)).toBe(1.234);
|
||||
expect(snapToStep(1.234, -1)).toBe(1.234);
|
||||
expect(snapToStep(NaN, 1)).toBeNaN();
|
||||
});
|
||||
});
|
||||
|
||||
describe('zoomFactor', () => {
|
||||
it('zooms in for a negative delta and out for a positive one', () => {
|
||||
expect(zoomFactor(-100)).toBeGreaterThan(1);
|
||||
|
||||
Reference in new issue
Block a user