feat(ui): auto-size batch size and yield inputs snug to content with click-to-highlight and pointer cursor
This commit is contained in:
@@ -166,6 +166,11 @@ export default function RecipeCalculator({ components, units, basisUnitId, yield
|
||||
}
|
||||
};
|
||||
|
||||
const currentScaleLabel = !showCustomInput && matchedScaleOption ? matchedScaleOption.label : "Custom";
|
||||
const yieldUnitSymbol = yieldUnits.find((u) => u.id === yieldDisplayUnitId)?.symbol ?? yieldDisplayUnitId;
|
||||
const yieldQtyString = String(roundForDisplay(scaledYield));
|
||||
const customFactorString = String(roundForDisplay(factor));
|
||||
|
||||
return (
|
||||
<section class="calculator" aria-label="Recipe scaling calculator">
|
||||
<div class="calculator-heading" data-testid="metaItem">
|
||||
@@ -176,6 +181,7 @@ export default function RecipeCalculator({ components, units, basisUnitId, yield
|
||||
aria-label="Batch size multiplier"
|
||||
class="meez-scale-select"
|
||||
data-testid="ScaleSelectorBase_scale_select"
|
||||
style={{ width: `${currentScaleLabel.length + 2.4}ch` }}
|
||||
value={!showCustomInput && matchedScaleOption ? String(matchedScaleOption.value) : "custom"}
|
||||
onChange={handleScaleSelectChange}
|
||||
>
|
||||
@@ -187,12 +193,14 @@ export default function RecipeCalculator({ components, units, basisUnitId, yield
|
||||
{showCustomInput && (
|
||||
<span class="custom-scale-wrap">
|
||||
<input
|
||||
type="number"
|
||||
min="0"
|
||||
step="any"
|
||||
type="text"
|
||||
inputMode="decimal"
|
||||
aria-label="Custom batch multiplier"
|
||||
class="custom-scale-input"
|
||||
value={roundForDisplay(factor)}
|
||||
style={{ width: `${Math.max(2, customFactorString.length + 0.8)}ch` }}
|
||||
value={customFactorString}
|
||||
onFocus={(event) => (event.currentTarget as HTMLInputElement).select()}
|
||||
onClick={(event) => (event.currentTarget as HTMLInputElement).select()}
|
||||
onInput={(event) => {
|
||||
const val = Number((event.currentTarget as HTMLInputElement).value);
|
||||
if (!isNaN(val) && val > 0) setFactor(val);
|
||||
@@ -208,14 +216,21 @@ export default function RecipeCalculator({ components, units, basisUnitId, yield
|
||||
<span class="scale-label">Yield:</span>
|
||||
<span class="recipe-scale-control quantity-control">
|
||||
<input
|
||||
type="number"
|
||||
min="0"
|
||||
step="any"
|
||||
type="text"
|
||||
inputMode="decimal"
|
||||
aria-label="Finished yield quantity"
|
||||
value={roundForDisplay(scaledYield)}
|
||||
style={{ width: `${Math.max(2, yieldQtyString.length + 0.5)}ch` }}
|
||||
value={yieldQtyString}
|
||||
onFocus={(event) => (event.currentTarget as HTMLInputElement).select()}
|
||||
onClick={(event) => (event.currentTarget as HTMLInputElement).select()}
|
||||
onInput={(event) => changeYield(Number((event.currentTarget as HTMLInputElement).value))}
|
||||
/>
|
||||
<select aria-label="Finished yield unit" value={yieldDisplayUnitId} onChange={(event) => setYieldDisplayUnitId((event.currentTarget as HTMLSelectElement).value)}>
|
||||
<select
|
||||
aria-label="Finished yield unit"
|
||||
value={yieldDisplayUnitId}
|
||||
style={{ width: `${yieldUnitSymbol.length + 2.2}ch` }}
|
||||
onChange={(event) => setYieldDisplayUnitId((event.currentTarget as HTMLSelectElement).value)}
|
||||
>
|
||||
{yieldUnits.map((unit) => <option value={unit.id} key={unit.id}>{unit.symbol}</option>)}
|
||||
</select>
|
||||
</span>
|
||||
@@ -283,7 +298,7 @@ export default function RecipeCalculator({ components, units, basisUnitId, yield
|
||||
)}
|
||||
</div>
|
||||
|
||||
{calculatePercent&&percentMode==="bakers"&&baseItems.length>0&&<section class="bakers-base-summary"><strong>Base</strong><div>{baseItems.map(item=>{const displayUnitId=lineUnits[item.id]??item.amount.unit_id;const displayQuantity=convertItem(item.amount.quantity*validFactor,item.amount.unit_id,displayUnitId,item,units);return <p><span><input aria-label={`${item.label} base quantity`} type="number" min="0" step="any" value={roundForDisplay(displayQuantity)} onInput={(event)=>{const canonicalQuantity=convertItem(Number(event.currentTarget.value),displayUnitId,item.amount.unit_id,item,units);setFactor(item.amount.quantity>0?canonicalQuantity/item.amount.quantity:1)}}/><select aria-label={`${item.label} base unit`} value={displayUnitId} onChange={(event)=>setLineUnits((current)=>({...current,[item.id]:event.currentTarget.value}))}>{itemUnits(item,units).map(unit=><option value={unit.id}>{unit.symbol}</option>)}</select></span><a href={item.href}>{item.label}</a></p>})}</div></section>}
|
||||
{calculatePercent&&percentMode==="bakers"&&baseItems.length>0&&<section class="bakers-base-summary"><strong>Base</strong><div>{baseItems.map(item=>{const displayUnitId=lineUnits[item.id]??item.amount.unit_id;const displayQuantity=convertItem(item.amount.quantity*validFactor,item.amount.unit_id,displayUnitId,item,units);return <p><span><input aria-label={`${item.label} base quantity`} type="text" inputMode="decimal" style={{ width: `${Math.max(2, String(roundForDisplay(displayQuantity)).length + 0.5)}ch` }} value={roundForDisplay(displayQuantity)} onFocus={(event) => (event.currentTarget as HTMLInputElement).select()} onClick={(event) => (event.currentTarget as HTMLInputElement).select()} onInput={(event)=>{const canonicalQuantity=convertItem(Number(event.currentTarget.value),displayUnitId,item.amount.unit_id,item,units);setFactor(item.amount.quantity>0?canonicalQuantity/item.amount.quantity:1)}}/><select aria-label={`${item.label} base unit`} value={displayUnitId} onChange={(event)=>setLineUnits((current)=>({...current,[item.id]:event.currentTarget.value}))}>{itemUnits(item,units).map(unit=><option value={unit.id}>{unit.symbol}</option>)}</select></span><a href={item.href}>{item.label}</a></p>})}</div></section>}
|
||||
{components.map((component) => (
|
||||
<div class="formula-component" key={component.id}>
|
||||
{Boolean(component.name?.trim()) && <h3 class="formula-component-heading">{component.name}</h3>}
|
||||
@@ -304,7 +319,7 @@ export default function RecipeCalculator({ components, units, basisUnitId, yield
|
||||
};
|
||||
return (
|
||||
<tr key={item.id} class={item.basisMember ? "basis-row" : ""}>
|
||||
<td><span class="quantity-control line-quantity"><input aria-label={`${item.label} quantity`} type="number" min="0" step="any" value={roundForDisplay(displayQuantity)} onInput={(event) => changeLineQuantity(Number(event.currentTarget.value))}/><select aria-label={`${item.label} unit`} value={displayUnitId} style={{width:`${(compatibleUnits.find((unit) => unit.id === displayUnitId)?.symbol ?? displayUnitId).length + 0.75}ch`}} onChange={(event) => { const next = event.currentTarget.value; try { convertItem(item.amount.quantity * validFactor, item.amount.unit_id, next, item, units); } catch { event.currentTarget.value = displayUnitId; return; } setLineUnits((current) => ({ ...current, [item.id]: next })); }}>{compatibleUnits.map((unit) => <option value={unit.id} key={unit.id}>{unit.symbol}</option>)}</select></span></td>
|
||||
<td><span class="quantity-control line-quantity"><input aria-label={`${item.label} quantity`} type="text" inputMode="decimal" style={{ width: `${Math.max(2, String(roundForDisplay(displayQuantity)).length + 0.5)}ch` }} value={roundForDisplay(displayQuantity)} onFocus={(event) => (event.currentTarget as HTMLInputElement).select()} onClick={(event) => (event.currentTarget as HTMLInputElement).select()} onInput={(event) => changeLineQuantity(Number(event.currentTarget.value))}/><select aria-label={`${item.label} unit`} value={displayUnitId} style={{width:`${(compatibleUnits.find((unit) => unit.id === displayUnitId)?.symbol ?? displayUnitId).length + 0.75}ch`}} onChange={(event) => { const next = event.currentTarget.value; try { convertItem(item.amount.quantity * validFactor, item.amount.unit_id, next, item, units); } catch { event.currentTarget.value = displayUnitId; return; } setLineUnits((current) => ({ ...current, [item.id]: next })); }}>{compatibleUnits.map((unit) => <option value={unit.id} key={unit.id}>{unit.symbol}</option>)}</select></span></td>
|
||||
<td>
|
||||
<span class="calculator-ingredient-name">
|
||||
{item.href ? <a href={item.href}>{item.label}</a> : item.label}
|
||||
|
||||
+29
-6
@@ -2983,11 +2983,13 @@ input[type="search"]::-webkit-search-results-decoration,
|
||||
cursor: pointer !important;
|
||||
outline: none !important;
|
||||
height: 24px !important;
|
||||
field-sizing: content;
|
||||
transition: border-color 0.15s ease !important;
|
||||
}
|
||||
|
||||
.application-body .recipe-view-formula .calculator-heading .meez-scale-select:hover {
|
||||
border-bottom-color: #050841 !important;
|
||||
cursor: pointer !important;
|
||||
}
|
||||
|
||||
.application-body .recipe-view-formula .calculator-heading .meez-scale-select:focus {
|
||||
@@ -3007,9 +3009,8 @@ input[type="search"]::-webkit-search-results-decoration,
|
||||
}
|
||||
|
||||
.application-body .recipe-view-formula .calculator-heading .custom-scale-input {
|
||||
width: 42px !important;
|
||||
min-width: 24px !important;
|
||||
max-width: 5ch !important;
|
||||
width: auto !important;
|
||||
min-width: 16px !important;
|
||||
font-family: var(--meez-font-sans) !important;
|
||||
font-size: 14px !important;
|
||||
font-weight: 500 !important;
|
||||
@@ -3019,14 +3020,19 @@ input[type="search"]::-webkit-search-results-decoration,
|
||||
outline: 0 !important;
|
||||
padding: 0 !important;
|
||||
text-align: right !important;
|
||||
cursor: pointer !important;
|
||||
field-sizing: content;
|
||||
-moz-appearance: textfield !important;
|
||||
}
|
||||
|
||||
.application-body .recipe-view-formula .calculator-heading .custom-scale-input:focus {
|
||||
cursor: text !important;
|
||||
}
|
||||
|
||||
.application-body .recipe-view-formula .calculator-heading .recipe-scale-control input,
|
||||
.application-body .immersive-main .recipe-read-shell .recipe-view-formula .calculator-heading .recipe-scale-control input {
|
||||
width: auto !important;
|
||||
min-width: 16px !important;
|
||||
max-width: 6ch !important;
|
||||
height: auto !important;
|
||||
padding: 0 !important;
|
||||
color: #050841 !important;
|
||||
@@ -3037,9 +3043,25 @@ input[type="search"]::-webkit-search-results-decoration,
|
||||
font-size: 15px !important;
|
||||
font-weight: 500 !important;
|
||||
text-align: left !important;
|
||||
cursor: pointer !important;
|
||||
field-sizing: content;
|
||||
-moz-appearance: textfield !important;
|
||||
}
|
||||
|
||||
.application-body .recipe-view-formula .calculator-heading .recipe-scale-control input:focus,
|
||||
.application-body .immersive-main .recipe-read-shell .recipe-view-formula .calculator-heading .recipe-scale-control input:focus {
|
||||
cursor: text !important;
|
||||
}
|
||||
|
||||
.application-body .recipe-ingredients .quantity-control input {
|
||||
cursor: pointer !important;
|
||||
field-sizing: content;
|
||||
}
|
||||
|
||||
.application-body .recipe-ingredients .quantity-control input:focus {
|
||||
cursor: text !important;
|
||||
}
|
||||
|
||||
.application-body .recipe-view-formula .calculator-heading .batch-unit-suffix {
|
||||
font-family: var(--meez-font-sans) !important;
|
||||
font-size: 14px !important;
|
||||
@@ -3052,10 +3074,10 @@ input[type="search"]::-webkit-search-results-decoration,
|
||||
.application-body .recipe-view-formula .calculator-heading .quantity-control input,
|
||||
.application-body .immersive-main .recipe-read-shell .recipe-view-formula .calculator-heading .quantity-control input {
|
||||
min-width: 24px !important;
|
||||
max-width: 6ch !important;
|
||||
font-size: 15px !important;
|
||||
font-weight: 500 !important;
|
||||
padding-right: 4px !important;
|
||||
padding-right: 2px !important;
|
||||
cursor: pointer !important;
|
||||
}
|
||||
|
||||
.application-body .recipe-view-formula .calculator-heading .recipe-scale-control select,
|
||||
@@ -3073,6 +3095,7 @@ input[type="search"]::-webkit-search-results-decoration,
|
||||
font-weight: 500 !important;
|
||||
cursor: pointer !important;
|
||||
text-align: left !important;
|
||||
field-sizing: content;
|
||||
}
|
||||
|
||||
/* Meez HelperTooltipBase_help_icon & Meez Tooltip Popups */
|
||||
|
||||
Reference in New Issue
Block a user