UNPKG

@canutin/svelte-currency-input

Version:

A form input that converts numbers to currencies as you type in localized formats

173 lines (130 loc) 7.67 kB
# svelte-currency-input A masked form input that converts numbers to localized currency formats as you type [<img width="962" alt="image" src="https://user-images.githubusercontent.com/1434675/190873948-c0385747-6fa9-4077-8bd5-717e4d1124a0.png">](https://svelte.dev/repl/d8f7d22e5b384555b430f62b157ac503?version=3.50.1) <p align="center"> 👩‍💻 Play with it on <a href="https://svelte.dev/repl/d8f7d22e5b384555b430f62b157ac503?version=3.50.1" target="_blank">REPL</a> &nbsp;—&nbsp; 💵 See it in a <a href="https://github.com/Canutin/desktop/blob/master/sveltekit/src/lib/components/FormCurrency.svelte" target="_blank">real project</a>! </p> --- ## Features - Formats **positive** and **negative** values - Leverages [`Intl.NumberFormat`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/NumberFormat) for **localizing** currency denominations and masking the input - Simple [API](#api) - Minimal default styling, easy to [customize](#styling) ## Usage ```bash npm install @canutin/svelte-currency-input --save ``` ```html <script lang="ts"> import CurrencyInput from '@canutin/svelte-currency-input'; </script> <CurrencyInput name="total" value={-420.69} locale="nl-NL" currency="EUR" /> ``` ## How it works When the form is submitted you get _unformatted_ or _formatted_ values from two `<input />`'s. This is more or less what `<CurrencyInput />` looks like under the hood: ```html <div class="currencyInput"> <!-- Unformatted value --> <input class="currencyInput__unformatted" type="hidden" name="total" value="-420.69" /> <!-- Formatted value --> <input class="currencyInput__formatted" type="text" name="formatted-total" value="€ -420,69" /> </div> ``` ## API Option | Type | Default | Description | ----------------- | --------------- | ----------- | ----------- | value | `number` | `undefined` | Initial value. If left `undefined` a formatted value of `0` is visible as a placeholder | locale | `string` | `en-US` | Overrides default locale. [Examples](https://gist.github.com/ncreated/9934896) | currency | `string` | `USD` | Overrides default currency. [Examples](https://www.xe.com/symbols/) | name | `string` | `total` | Applies the name to the [input fields](#how-it-works) for _unformatted_ (e.g `[name=total]`) and _formatted_ (e.g. `[name=formatted-total]`) values | id | `string` | `undefined` | Sets the `id` attribute on the input | required | `boolean` | `false` | Marks the input as required | disabled | `boolean` | `false` | Marks the input as disabled | placeholder | `string` `number` `null` | `0` | A `string` will override the default placeholder. A `number` will override it by formatting it to the set currency. Setting it to `null` will not show a placeholder | isZeroNullish | `boolean` | `false` | If `true` and when the value is `0`, it will override the default placeholder and render the formatted value in the field like any other value. _Note: this option might become the default in future versions_ | autocomplete | `string` | `undefined` | Sets the autocomplete attribute. Accepts any valid HTML [autocomplete attribute values](https://developer.mozilla.org/en-US/docs/Web/HTML/Attributes/autocomplete#values) | isNegativeAllowed | `boolean` | `true` | If `false`, forces formatting only to positive values and ignores `--positive` and `--negative` styling modifiers | fractionDigits | `number` | `2` | Sets `maximumFractionDigits` in [`Intl.NumberFormat()` constructor](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/NumberFormat/NumberFormat#minimumfractiondigits) used for formatting the currency. Supported digits: `0` to `20` | inputClasses | `object` | [See below](#Styling) | Selectively overrides any class names passed | onValueChange | `Callback` | `undefined` | Runs a callback function after the value changes | ## Styling There are two ways of customizing the styling of the input: 1. Passing it your own CSS classes 2. Overriding the styles using the existing class names You can **override all of the class names** by passing an object to `inputClasses` that has **one or more** of these properties: ```typescript interface InputClasses { wrapper?: string; // <div> that contains the two <input> elements unformatted?: string; // <input type="hidden"> that contains the unformatted value formatted?: string; // <input type="text"> that contains the formatted value formattedPositive?: string; // Class added when the formatted input is positive formattedNegative?: string; // Class added when the formatted input is negative formattedZero?: string; // Class added when the formatted input is zero } ``` Usage (with [Tailwind CSS](https://tailwindcss.com/) as an example): ```svelte <CurrencyInput name="total" value="{420.69}" inputClasses={ { wrapper: "form-control block", formatted: 'py-1.5 text-gray-700', formattedPositive: 'text-green-700', formattedNegative: 'text-red-700' } } /> ``` Alternatively you can **write your own CSS** by overriding the [default styles](https://github.com/canutin/svelte-currency-input/blob/main/src/lib/CurrencyInput.svelte) which use [BEM naming conventions](https://getbem.com/naming/). To do so apply your styles as shown below: ```svelte <div class="my-currency-input"> <CurrencyInput name="total" value="{420.69}" /> </div> <style> /* Container */ div.my-currency-input :global(div.currencyInput) { /* ... */ } /* Formatted input */ div.my-currency-input :global(input.currencyInput__formatted) { /* ... */ } /* Formatted input when the it's disabled */ div.my-currency-input :global(input.currencyInput__formatted:disabled) { /* ... */ } /* Formatted input when the value is zero */ div.my-currency-input :global(input.currencyInput__formatted--zero) { /* ... */ } /* Formatted input when the value is positive */ div.my-currency-input :global(input.currencyInput__formatted--positive) { /* ... */ } /* Formatted input when the value is negative */ div.my-currency-input :global(input.currencyInput__formatted--negative) { /* ... */ } </style> ``` ## Contributing Here's ways in which you can contribute: - Found a bug? Open a [new issue](https://github.com/canutin/svelte-currency-input/issues/new) - Comment or upvote [existing issues](https://github.com/canutin/svelte-currency-input/issues) - Submit a [pull request](https://github.com/canutin/svelte-currency-input/pulls) ## Developing This package was generated with [SvelteKit](https://kit.svelte.dev/). Install dependencies with `npm install`, then start a development server: ```bash npm run dev # or start the server and open the app in a new browser tab npm run dev -- --open ``` #### Integration tests The component is tested using [Playwright](https://playwright.dev/). You can find the tests in [`tests/svelte-currency-input.test.ts`](https://github.com/Canutin/svelte-currency-input/blob/main/tests/svelte-currency-input.test.ts) To run all tests on **Chromium**, **Firefox** and **Webkit**: ```bash npm run test ``` To run all tests on a specific browser (e.g. **Webkit**): ```bash npx playwright test --project=webkit ``` Additional debug commands can be found on [Playwright's documentation](https://playwright.dev/docs/test-cli).