Skip to content

Commit

Permalink
fix: Input not reformatting after external value changes (#75)
Browse files Browse the repository at this point in the history
* #74 - Format with min fraction digits, except when the field is active

* add chained inputs to demo page

* update existing tests

* test: chained inputs have the correct behavior

* apply formatting

* update test comment
  • Loading branch information
fmaclen authored Feb 13, 2024
1 parent 7d5c21b commit 32b9f1e
Show file tree
Hide file tree
Showing 3 changed files with 210 additions and 71 deletions.
44 changes: 32 additions & 12 deletions src/lib/CurrencyInput.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -78,12 +78,20 @@
// Formats the value when the input loses focus and sets the correct number of
// fraction digits when the value is zero
const handleOnBlur = () => setFormattedValue(true);
const handleOnBlur = () => setFormattedValue();
// Also set the correct fraction digits when the value is zero on initial load
onMount(() => setFormattedValue(true));
let dom: Document;
let inputElement: HTMLInputElement;
let inputTarget: HTMLInputElement;
onMount(() => {
// Set the document object as a variable so we know the page has mounted
dom = document;
// Set the correct fraction digits when the value is zero on initial load
setFormattedValue();
});
let inputTarget: EventTarget | null;
const currencyDecimal = new Intl.NumberFormat(locale).format(1.1).charAt(1); // '.' or ','
const isDecimalComma = currencyDecimal === ',';
const currencySymbol = formatCurrency(0, 0)
Expand All @@ -110,7 +118,7 @@
if (ignoreSymbols.includes(strippedUnformattedValue)) return;
// Set the starting caret positions
inputTarget = event.target as HTMLInputElement;
inputTarget = event.target;
// Reverse the value when minus is pressed
if (isNegativeAllowed && event.key === '-') value = value * -1;
Expand Down Expand Up @@ -147,13 +155,23 @@
}
};
const setFormattedValue = (hasMinFractionDigits?: boolean) => {
const setFormattedValue = () => {
// Do nothing because the page hasn't mounted yet
if (!dom) return;
// Previous caret position
const startCaretPosition = inputTarget?.selectionStart || 0;
const startCaretPosition = inputElement?.selectionStart || 0;
const previousFormattedValueLength = formattedValue.length;
// Apply formatting to input
formattedValue = isZero && !isZeroNullish ? '' : formatCurrency(value, fractionDigits, hasMinFractionDigits ? fractionDigits : 0);
formattedValue =
isZero && !isZeroNullish
? ''
: formatCurrency(
value,
fractionDigits,
dom.activeElement === inputElement ? 0 : fractionDigits
);
// Update `value` after formatting
setUnformattedValue();
Expand All @@ -164,16 +182,17 @@
if (previousFormattedValueLength !== formattedValue.length) {
const endCaretPosition =
startCaretPosition + formattedValue.length - previousFormattedValueLength;
inputTarget?.setSelectionRange(endCaretPosition, endCaretPosition);
inputElement?.setSelectionRange(endCaretPosition, endCaretPosition);
}
// Run callback function when `value` changes
onValueChange(value);
};
const handlePlaceholder = (placeholder: string | number | null) => {
if (typeof placeholder === "number") return formatCurrency(placeholder, fractionDigits, fractionDigits);
if (placeholder === null) return "";
if (typeof placeholder === 'number')
return formatCurrency(placeholder, fractionDigits, fractionDigits);
if (placeholder === null) return '';
return placeholder;
};
Expand Down Expand Up @@ -204,12 +223,13 @@
: ''}
"
type="text"
inputmode={fractionDigits > 0 ? "decimal" : "numeric"}
inputmode={fractionDigits > 0 ? 'decimal' : 'numeric'}
name={`formatted-${name}`}
required={required && !isZero}
placeholder={handlePlaceholder(placeholder)}
{autocomplete}
{disabled}
bind:this={inputElement}
bind:value={formattedValue}
on:keydown={handleKeyDown}
on:keyup={setUnformattedValue}
Expand Down
172 changes: 117 additions & 55 deletions src/routes/+page.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,11 @@
};
let unchangedValue = 999; // Used for onValueChange()
let chainedValue = 9999.99; // Used for chained inputs
const setChainedValue = () => {
chainedValue = 420.69;
};
</script>

<form class="demoForm" on:submit={handleSubmit}>
Expand Down Expand Up @@ -43,61 +48,105 @@
</nav>

<div class="demoForm__container">
<CurrencyInput name="default" value={-42069.69} />
<CurrencyInput name="colon" locale="es-CR" currency="CRC" />
<CurrencyInput
name="pound"
value={1234.56}
isNegativeAllowed={false}
placeholder={null}
locale="en-GB"
currency="GBP"
/>
<CurrencyInput
name="bitcoin"
value={0.87654321}
locale="th-TH"
currency="THB"
fractionDigits={8}
/>

<CurrencyInput name="yen" value={5678.9} locale="en-JP" currency="JPY" />
<CurrencyInput name="shekel" value={97532.95} disabled={true} locale="il-IL" currency="ILS" />
<CurrencyInput name="euro" value={-42069.69} locale="nl-NL" currency="EUR" />
<CurrencyInput
name="won"
placeholder={1234.56}
isNegativeAllowed={false}
locale="ko-KO"
currency="KRW"
inputClasses={{
wrapper: 'currencyInput custom-wrapper-class',
unformatted: 'custom-unformatted-class'
}}
/>
<CurrencyInput
name="pesos"
value={unchangedValue}
isNegativeAllowed={false}
placeholder={null}
autocomplete="off"
locale="es-AR"
currency="ARS"
onValueChange={(value) => {
// Prevent alerting on initial load
if (unchangedValue !== value) {
unchangedValue = value; // Update the unchanged value
if (browser) window.alert(`The value for ARS has changed to: ${value}`); // Alert the user
}
}}
/>
<CurrencyInput name="rupees" value={678} locale="hi-IN" currency="INR" fractionDigits={3} />
<CurrencyInput name="soles" value={0} isZeroNullish={true} placeholder={null} locale="es-PE" currency="PEN" />
<CurrencyInput name="dinars" value={0} placeholder={"How many Dinars?"} locale="en-US" currency="RSD" fractionDigits={0} />
<fieldset class="demoForm__fieldset">
<legend class="demoForm__legend">Stand-alone inputs</legend>
<CurrencyInput name="default" value={-42069.69} />
<CurrencyInput name="colon" locale="es-CR" currency="CRC" />
<CurrencyInput
name="pound"
value={1234.56}
isNegativeAllowed={false}
placeholder={null}
locale="en-GB"
currency="GBP"
/>
<CurrencyInput
name="bitcoin"
value={0.87654321}
locale="th-TH"
currency="THB"
fractionDigits={8}
/>

<CurrencyInput name="yen" value={5678.9} locale="en-JP" currency="JPY" />
<CurrencyInput name="shekel" value={97532.95} disabled={true} locale="il-IL" currency="ILS" />
<CurrencyInput name="euro" value={-42069.69} locale="nl-NL" currency="EUR" />
<CurrencyInput
name="won"
placeholder={1234.56}
isNegativeAllowed={false}
locale="ko-KO"
currency="KRW"
inputClasses={{
wrapper: 'currencyInput custom-wrapper-class',
unformatted: 'custom-unformatted-class'
}}
/>
<CurrencyInput
name="pesos"
value={unchangedValue}
isNegativeAllowed={false}
placeholder={null}
autocomplete="off"
locale="es-AR"
currency="ARS"
onValueChange={(value) => {
// Prevent alerting on initial load
if (unchangedValue !== value) {
unchangedValue = value; // Update the unchanged value
if (browser) window.alert(`The value for ARS has changed to: ${value}`); // Alert the user
}
}}
/>
<CurrencyInput name="rupees" value={678} locale="hi-IN" currency="INR" fractionDigits={3} />
<CurrencyInput
name="soles"
value={0}
isZeroNullish={true}
placeholder={null}
locale="es-PE"
currency="PEN"
/>
<CurrencyInput
name="dinars"
value={0}
placeholder={'How many Dinars?'}
locale="en-US"
currency="RSD"
fractionDigits={0}
/>
</fieldset>

<fieldset class="demoForm__fieldset">
<legend class="demoForm__legend">Chained inputs</legend>
<CurrencyInput
name="chained-east-caribbean-dollar"
bind:value={chainedValue}
locale="aig"
currency="XCD"
fractionDigits={4}
/>
<CurrencyInput name="chained-euros" bind:value={chainedValue} locale="nl-NL" currency="EUR" />
<CurrencyInput
name="chained-dollars"
bind:value={chainedValue}
locale="en-US"
currency="USD"
fractionDigits={0}
/>
<button
id="set-chained-value"
type="button"
class="demoForm__button"
on:click={setChainedValue}
>
Set chained value to <strong>420.69</strong>
</button>
</fieldset>
</div>

<nav class="demoForm__output">
<button type="submit" class="demoForm__submit">Submit form</button>
<button id="submit-form" type="submit" class="demoForm__button">Submit form</button>

<pre class="demoForm__pre {!output && 'demoForm__pre--placeholder'}">{output
? output
Expand Down Expand Up @@ -144,22 +193,35 @@
}
div.demoForm__container {
display: flex;
flex-direction: column;
gap: 32px;
}
fieldset.demoForm__fieldset {
display: grid;
grid-template-columns: repeat(4, 1fr);
align-items: center;
justify-content: center;
gap: calc(var(--gap) / 2);
height: max-content;
border: 1px solid #ccc;
padding: 16px;
@media (max-width: 768px) {
grid-template-columns: repeat(2, 1fr);
}
@media (max-width: 512px) {
grid-template-columns: 1fr;
}
}
legend.demoForm__legend {
font-size: 13px;
color: #666;
}
h1.demoForm__h1 {
color: #333;
font-size: 20px;
Expand Down Expand Up @@ -224,7 +286,7 @@
font-size: 13px;
}
button.demoForm__submit {
button.demoForm__button {
border: none;
background-color: #333;
color: #fff;
Expand All @@ -234,7 +296,7 @@
height: max-content;
}
button.demoForm__submit:hover {
button.demoForm__button:hover {
background-color: #000;
}
</style>
Loading

0 comments on commit 32b9f1e

Please sign in to comment.