Fix: shift+click preserves input value, Ctrl+click adopts value, stable highlight layout
- shift+click now only sets fromUnitId; inputValue is unchanged so all outputs recalculate correctly from the original user input - Ctrl+click is a new gesture that sets both fromUnitId and inputValue to the tile's displayed converted value (the "pivot" action) - highlighted-result block always renders with min-height:8rem so the page below it never jumps when a highlight is added or cleared - hotkey hint updated to document all three click gestures Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -97,6 +97,7 @@
|
|||||||
align-items: center;
|
align-items: center;
|
||||||
gap: 0.4rem;
|
gap: 0.4rem;
|
||||||
position: relative;
|
position: relative;
|
||||||
|
min-height: 8rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.highlighted-value {
|
.highlighted-value {
|
||||||
|
|||||||
@@ -14,7 +14,8 @@
|
|||||||
highlighted,
|
highlighted,
|
||||||
isInputUnit = false,
|
isInputUnit = false,
|
||||||
onclick,
|
onclick,
|
||||||
onshiftclick
|
onshiftclick,
|
||||||
|
onctrlclick
|
||||||
}: {
|
}: {
|
||||||
value: Big;
|
value: Big;
|
||||||
unit: Unit;
|
unit: Unit;
|
||||||
@@ -22,14 +23,18 @@
|
|||||||
isInputUnit?: boolean;
|
isInputUnit?: boolean;
|
||||||
onclick: () => void;
|
onclick: () => void;
|
||||||
onshiftclick: () => void;
|
onshiftclick: () => void;
|
||||||
|
onctrlclick: () => void;
|
||||||
} = $props();
|
} = $props();
|
||||||
|
|
||||||
// Compute once per reactive update; shared between aria-label and display span.
|
// Compute once per reactive update; shared between aria-label and display span.
|
||||||
let formattedValue = $derived(formatBig(value));
|
let formattedValue = $derived(formatBig(value));
|
||||||
|
|
||||||
function handleClick(e: MouseEvent) {
|
function handleClick(e: MouseEvent) {
|
||||||
if (e.shiftKey) {
|
if (e.ctrlKey) {
|
||||||
// Shift+click promotes this tile to the from-unit.
|
// Ctrl+click adopts this tile's value as the new input value and promotes it to from-unit.
|
||||||
|
onctrlclick();
|
||||||
|
} else if (e.shiftKey) {
|
||||||
|
// Shift+click promotes this tile to the from-unit (inputValue unchanged).
|
||||||
// Guard: if it already IS the from-unit, the action would be a no-op.
|
// Guard: if it already IS the from-unit, the action would be a no-op.
|
||||||
if (!isInputUnit) onshiftclick();
|
if (!isInputUnit) onshiftclick();
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import type { Unit, ResultItem } from '$lib/types';
|
import type { Unit, ResultItem } from '$lib/types';
|
||||||
|
import type { Big } from 'big.js';
|
||||||
import { MIN_COLUMN_WIDTH, GRID_GAP } from '$lib/config';
|
import { MIN_COLUMN_WIDTH, GRID_GAP } from '$lib/config';
|
||||||
import UnitInput from './UnitInput.svelte';
|
import UnitInput from './UnitInput.svelte';
|
||||||
import UnitDropdown from './UnitDropdown.svelte';
|
import UnitDropdown from './UnitDropdown.svelte';
|
||||||
@@ -19,7 +20,8 @@
|
|||||||
oninputchange,
|
oninputchange,
|
||||||
onunitchange,
|
onunitchange,
|
||||||
onhighlight,
|
onhighlight,
|
||||||
onsetinput
|
onsetinput,
|
||||||
|
onctrlclick
|
||||||
}: {
|
}: {
|
||||||
units: Unit[];
|
units: Unit[];
|
||||||
inputValue: number;
|
inputValue: number;
|
||||||
@@ -30,6 +32,7 @@
|
|||||||
onunitchange: (id: string) => void;
|
onunitchange: (id: string) => void;
|
||||||
onhighlight: (id: string) => void;
|
onhighlight: (id: string) => void;
|
||||||
onsetinput: (id: string) => void;
|
onsetinput: (id: string) => void;
|
||||||
|
onctrlclick: (id: string, value: Big) => void;
|
||||||
} = $props();
|
} = $props();
|
||||||
|
|
||||||
const gridStyle = `--min-col-width: ${MIN_COLUMN_WIDTH}; --grid-gap: ${GRID_GAP};`;
|
const gridStyle = `--min-col-width: ${MIN_COLUMN_WIDTH}; --grid-gap: ${GRID_GAP};`;
|
||||||
@@ -50,11 +53,12 @@
|
|||||||
isInputUnit={item.unit.id === fromUnitId}
|
isInputUnit={item.unit.id === fromUnitId}
|
||||||
onclick={() => onhighlight(item.unit.id)}
|
onclick={() => onhighlight(item.unit.id)}
|
||||||
onshiftclick={() => onsetinput(item.unit.id)}
|
onshiftclick={() => onsetinput(item.unit.id)}
|
||||||
|
onctrlclick={() => onctrlclick(item.unit.id, item.convertedValue)}
|
||||||
/>
|
/>
|
||||||
{/each}
|
{/each}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<p class="hotkey-hint">
|
<p class="hotkey-hint">
|
||||||
<small>Click a result to highlight it · Shift+click to use it as input</small>
|
<small>Click to highlight · Shift+click to change input unit · Ctrl+click to adopt value as input</small>
|
||||||
</p>
|
</p>
|
||||||
</article>
|
</article>
|
||||||
|
|||||||
@@ -89,9 +89,14 @@
|
|||||||
|
|
||||||
function handleSetInput(id: string) {
|
function handleSetInput(id: string) {
|
||||||
// Shift+click: promote result unit to from-unit.
|
// Shift+click: promote result unit to from-unit.
|
||||||
// Re-express the current value in terms of the new from-unit.
|
// inputValue is NOT changed — outputs recalculate from the same inputValue with the new fromUnitId.
|
||||||
// highlightedUnitId is NOT cleared — the two states are independent.
|
// highlightedUnitId is NOT cleared — the two states are independent.
|
||||||
const convertedValue = convertById(inputValue, fromUnitId, id, units);
|
fromUnitId = id;
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleCtrlClick(id: string, convertedValue: import('big.js').Big) {
|
||||||
|
// Ctrl+click: adopt this tile's displayed value as the new inputValue and promote it to from-unit.
|
||||||
|
// This is the "pivot" gesture.
|
||||||
inputValue = parseFloat(convertedValue.toFixed(FRACTIONAL_DIGITS));
|
inputValue = parseFloat(convertedValue.toFixed(FRACTIONAL_DIGITS));
|
||||||
fromUnitId = id;
|
fromUnitId = id;
|
||||||
}
|
}
|
||||||
@@ -101,8 +106,8 @@
|
|||||||
<title>Humor Units — Converter</title>
|
<title>Humor Units — Converter</title>
|
||||||
</svelte:head>
|
</svelte:head>
|
||||||
|
|
||||||
{#if highlightedResult !== null}
|
<div class="highlighted-result" aria-live="polite">
|
||||||
<div class="highlighted-result">
|
{#if highlightedResult !== null}
|
||||||
<button
|
<button
|
||||||
class="highlight-dismiss"
|
class="highlight-dismiss"
|
||||||
aria-label="Remove highlight"
|
aria-label="Remove highlight"
|
||||||
@@ -111,8 +116,8 @@
|
|||||||
<span class="highlighted-value">{formatBig(highlightedResult.convertedValue)}</span>
|
<span class="highlighted-value">{formatBig(highlightedResult.convertedValue)}</span>
|
||||||
<span class="highlighted-label">{highlightedResult.unit.labelPlural}</span>
|
<span class="highlighted-label">{highlightedResult.unit.labelPlural}</span>
|
||||||
<span class="highlighted-symbol">{highlightedResult.unit.symbol}</span>
|
<span class="highlighted-symbol">{highlightedResult.unit.symbol}</span>
|
||||||
</div>
|
{/if}
|
||||||
{/if}
|
</div>
|
||||||
|
|
||||||
<ConverterCard
|
<ConverterCard
|
||||||
{units}
|
{units}
|
||||||
@@ -124,4 +129,5 @@
|
|||||||
onunitchange={handleUnitChange}
|
onunitchange={handleUnitChange}
|
||||||
onhighlight={handleHighlight}
|
onhighlight={handleHighlight}
|
||||||
onsetinput={handleSetInput}
|
onsetinput={handleSetInput}
|
||||||
|
onctrlclick={handleCtrlClick}
|
||||||
/>
|
/>
|
||||||
|
|||||||
Reference in New Issue
Block a user