@transferwise/components 0.0.0-experimental-89ae24f → 0.0.0-experimental-c42d7d6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/build/i18n/en.json +2 -0
- package/build/i18n/en.json.js +2 -0
- package/build/i18n/en.json.js.map +1 -1
- package/build/i18n/en.json.mjs +2 -0
- package/build/i18n/en.json.mjs.map +1 -1
- package/build/index.js +2 -0
- package/build/index.js.map +1 -1
- package/build/index.mjs +1 -0
- package/build/index.mjs.map +1 -1
- package/build/main.css +58 -0
- package/build/moneyInputField/AmountInput.js +281 -0
- package/build/moneyInputField/AmountInput.js.map +1 -0
- package/build/moneyInputField/AmountInput.mjs +279 -0
- package/build/moneyInputField/AmountInput.mjs.map +1 -0
- package/build/moneyInputField/AnimatedNumber.js +50 -0
- package/build/moneyInputField/AnimatedNumber.js.map +1 -0
- package/build/moneyInputField/AnimatedNumber.mjs +48 -0
- package/build/moneyInputField/AnimatedNumber.mjs.map +1 -0
- package/build/moneyInputField/Chevron.js +33 -0
- package/build/moneyInputField/Chevron.js.map +1 -0
- package/build/moneyInputField/Chevron.mjs +31 -0
- package/build/moneyInputField/Chevron.mjs.map +1 -0
- package/build/moneyInputField/CurrencySelector.js +160 -0
- package/build/moneyInputField/CurrencySelector.js.map +1 -0
- package/build/moneyInputField/CurrencySelector.mjs +157 -0
- package/build/moneyInputField/CurrencySelector.mjs.map +1 -0
- package/build/moneyInputField/MoneyInputField.js +113 -0
- package/build/moneyInputField/MoneyInputField.js.map +1 -0
- package/build/moneyInputField/MoneyInputField.messages.js +17 -0
- package/build/moneyInputField/MoneyInputField.messages.js.map +1 -0
- package/build/moneyInputField/MoneyInputField.messages.mjs +13 -0
- package/build/moneyInputField/MoneyInputField.messages.mjs.map +1 -0
- package/build/moneyInputField/MoneyInputField.mjs +109 -0
- package/build/moneyInputField/MoneyInputField.mjs.map +1 -0
- package/build/moneyInputField/useFocus.js +37 -0
- package/build/moneyInputField/useFocus.js.map +1 -0
- package/build/moneyInputField/useFocus.mjs +35 -0
- package/build/moneyInputField/useFocus.mjs.map +1 -0
- package/build/moneyInputField/useInputStyle.js +71 -0
- package/build/moneyInputField/useInputStyle.js.map +1 -0
- package/build/moneyInputField/useInputStyle.mjs +69 -0
- package/build/moneyInputField/useInputStyle.mjs.map +1 -0
- package/build/moneyInputField/utils.js +87 -0
- package/build/moneyInputField/utils.js.map +1 -0
- package/build/moneyInputField/utils.mjs +78 -0
- package/build/moneyInputField/utils.mjs.map +1 -0
- package/build/styles/main.css +58 -0
- package/build/styles/moneyInputField/AmountInput.css +32 -0
- package/build/styles/moneyInputField/Chevron.css +12 -0
- package/build/styles/moneyInputField/CurrencySelector.css +6 -0
- package/build/styles/moneyInputField/MoneyInputField.css +58 -0
- package/build/types/index.d.ts +2 -0
- package/build/types/index.d.ts.map +1 -1
- package/build/types/moneyInputField/AmountInput.d.ts +13 -0
- package/build/types/moneyInputField/AmountInput.d.ts.map +1 -0
- package/build/types/moneyInputField/AnimatedNumber.d.ts +9 -0
- package/build/types/moneyInputField/AnimatedNumber.d.ts.map +1 -0
- package/build/types/moneyInputField/Chevron.d.ts +6 -0
- package/build/types/moneyInputField/Chevron.d.ts.map +1 -0
- package/build/types/moneyInputField/CurrencySelector.d.ts +30 -0
- package/build/types/moneyInputField/CurrencySelector.d.ts.map +1 -0
- package/build/types/moneyInputField/MoneyInputField.d.ts +30 -0
- package/build/types/moneyInputField/MoneyInputField.d.ts.map +1 -0
- package/build/types/moneyInputField/MoneyInputField.messages.d.ts +12 -0
- package/build/types/moneyInputField/MoneyInputField.messages.d.ts.map +1 -0
- package/build/types/moneyInputField/index.d.ts +3 -0
- package/build/types/moneyInputField/index.d.ts.map +1 -0
- package/build/types/moneyInputField/useFocus.d.ts +7 -0
- package/build/types/moneyInputField/useFocus.d.ts.map +1 -0
- package/build/types/moneyInputField/useInputStyle.d.ts +10 -0
- package/build/types/moneyInputField/useInputStyle.d.ts.map +1 -0
- package/build/types/moneyInputField/useSelectionRange.d.ts +10 -0
- package/build/types/moneyInputField/useSelectionRange.d.ts.map +1 -0
- package/build/types/moneyInputField/utils.d.ts +22 -0
- package/build/types/moneyInputField/utils.d.ts.map +1 -0
- package/build/types/test-utils/index.d.ts +4 -0
- package/build/types/test-utils/index.d.ts.map +1 -1
- package/package.json +9 -2
- package/src/i18n/en.json +2 -0
- package/src/index.ts +2 -0
- package/src/main.css +58 -0
- package/src/main.less +1 -0
- package/src/moneyInputField/AmountInput.css +32 -0
- package/src/moneyInputField/AmountInput.less +43 -0
- package/src/moneyInputField/AmountInput.tsx +353 -0
- package/src/moneyInputField/AnimatedNumber.tsx +40 -0
- package/src/moneyInputField/Chevron.css +12 -0
- package/src/moneyInputField/Chevron.less +13 -0
- package/src/moneyInputField/Chevron.tsx +35 -0
- package/src/moneyInputField/CurrencySelector.css +6 -0
- package/src/moneyInputField/CurrencySelector.less +7 -0
- package/src/moneyInputField/CurrencySelector.tsx +218 -0
- package/src/moneyInputField/MoneyInputField.css +58 -0
- package/src/moneyInputField/MoneyInputField.less +13 -0
- package/src/moneyInputField/MoneyInputField.messages.ts +13 -0
- package/src/moneyInputField/MoneyInputField.story.tsx +188 -0
- package/src/moneyInputField/MoneyInputField.tsx +124 -0
- package/src/moneyInputField/index.ts +2 -0
- package/src/moneyInputField/useFocus.ts +35 -0
- package/src/moneyInputField/useInputStyle.ts +85 -0
- package/src/moneyInputField/useSelectionRange.ts +23 -0
- package/src/moneyInputField/utils.spec.ts +114 -0
- package/src/moneyInputField/utils.ts +116 -0
- package/src/ssr.spec.tsx +1 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"MoneyInputField.mjs","sources":["../../src/moneyInputField/MoneyInputField.tsx"],"sourcesContent":["import Body from '../body';\nimport { Label } from '../label/Label';\nimport { clsx } from 'clsx';\nimport { AnimatePresence, motion } from 'framer-motion';\nimport { useId, type ReactNode } from 'react';\n\nimport { type Props as CurrencySelectorProps, CurrencySelector } from './CurrencySelector';\nimport { CommonProps } from '../common';\nimport { AmountInput } from './AmountInput';\nimport { Chevron } from './Chevron';\nimport { InlinePrompt, type InlinePromptProps } from '../prompt/InlinePrompt';\n\ntype AmountType = number | null;\nexport type CurrencyType = string;\n\ntype DefaultCurrencySelectorInstanceType = Pick<\n CurrencySelectorProps,\n 'addons' | 'options' | 'onChange' | 'onOpen' | 'onSearchChange'\n>;\ntype CustomCurrencySelectorInstanceType = (props: { id: string; labelId: string }) => ReactNode;\ntype CurrencySelectorType =\n | DefaultCurrencySelectorInstanceType\n | CustomCurrencySelectorInstanceType;\n\nexport type Props = {\n label?: ReactNode;\n currencySelector?: CurrencySelectorType;\n amount?: AmountType;\n currency: CurrencyType;\n inlinePrompt?: {\n sentiment?: InlinePromptProps['sentiment'];\n message: InlinePromptProps['children'];\n };\n showChevron?: boolean;\n autoFocus?: boolean;\n loading?: boolean;\n onAmountChange: (amount: AmountType) => void;\n onFocusChange?: (focused: boolean) => void;\n} & CommonProps;\n\nexport default function MoneyInputField({\n label,\n currency,\n currencySelector = { options: [] } as DefaultCurrencySelectorInstanceType,\n amount,\n onAmountChange,\n className,\n inlinePrompt,\n showChevron,\n autoFocus,\n loading,\n onFocusChange,\n}: Props) {\n const inputId = useId();\n const labelId = useId();\n const customAlertId = useId();\n const currencyId = useId();\n\n const selector =\n typeof currencySelector === 'function' ? (\n currencySelector({ id: currencyId, labelId })\n ) : (\n <CurrencySelector\n id={currencyId}\n labelId={labelId}\n currency={currency}\n {...currencySelector}\n />\n );\n\n return (\n <div className={clsx('wds-money-input-field', className)}>\n <Label id={labelId} htmlFor={inputId} className={clsx('m-b-1', 'font-weight-normal')}>\n {label}\n </Label>\n <div\n className={clsx('d-flex')}\n role=\"group\"\n aria-labelledby={labelId}\n {...(inlinePrompt ? { 'aria-describedby': customAlertId } : {})}\n >\n <div className=\"wds-money-input-field-currency-selector\">{selector}</div>\n <AmountInput\n id={inputId}\n describedById={currencyId}\n amount={amount}\n currency={currency}\n // eslint-disable-next-line jsx-a11y/no-autofocus\n autoFocus={autoFocus}\n loading={loading}\n onChange={onAmountChange}\n onFocusChange={onFocusChange}\n />\n <div className={clsx('d-flex align-items-center', 'wds-money-input-field-chevron')}>\n <Chevron shouldShow={Boolean(showChevron)} />\n </div>\n </div>\n <AnimatePresence initial={false}>\n {inlinePrompt && (\n <div className={clsx('d-flex justify-content-end', inlinePrompt && 'm-t-1')}>\n <motion.div\n key={customAlertId}\n initial={{ opacity: 0, height: 0 }}\n animate={{\n opacity: 1,\n height: 'auto',\n transition: { delay: 0.75, duration: 0.3 },\n }}\n exit={{ opacity: 0, height: 0 }}\n >\n {inlinePrompt.sentiment ? (\n <InlinePrompt id={customAlertId} sentiment={inlinePrompt.sentiment}>\n {inlinePrompt.message}\n </InlinePrompt>\n ) : (\n <Body>{inlinePrompt.message}</Body>\n )}\n </motion.div>\n </div>\n )}\n </AnimatePresence>\n </div>\n );\n}\n"],"names":["MoneyInputField","label","currency","currencySelector","options","amount","onAmountChange","className","inlinePrompt","showChevron","autoFocus","loading","onFocusChange","inputId","useId","labelId","customAlertId","currencyId","selector","id","_jsx","CurrencySelector","_jsxs","clsx","children","Label","htmlFor","role","AmountInput","describedById","onChange","Chevron","shouldShow","Boolean","AnimatePresence","initial","motion","div","opacity","height","animate","transition","delay","duration","exit","sentiment","InlinePrompt","message","Body"],"mappings":"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"}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var React = require('react');
|
|
4
|
+
|
|
5
|
+
const focusTimeout = 5 * 1000;
|
|
6
|
+
const useFocus = () => {
|
|
7
|
+
const [focus, setFocus] = React.useState(false);
|
|
8
|
+
const [visualFocus, setVisualFocus] = React.useState(false);
|
|
9
|
+
const [counter, setCounter] = React.useState(0);
|
|
10
|
+
React.useEffect(() => {
|
|
11
|
+
if (focus) {
|
|
12
|
+
const timeout = setTimeout(() => {
|
|
13
|
+
setVisualFocus(false);
|
|
14
|
+
}, focusTimeout);
|
|
15
|
+
return () => clearTimeout(timeout);
|
|
16
|
+
}
|
|
17
|
+
}, [focus, counter]);
|
|
18
|
+
return {
|
|
19
|
+
focus,
|
|
20
|
+
setFocus: value => {
|
|
21
|
+
setFocus(value);
|
|
22
|
+
if (value) {
|
|
23
|
+
setVisualFocus(true);
|
|
24
|
+
}
|
|
25
|
+
// any call to setFocus should reset the timeout, even if the input was already in focus
|
|
26
|
+
// updating the counter will trigger the useEffect to reset the timeout
|
|
27
|
+
setCounter(prev => prev + 1);
|
|
28
|
+
},
|
|
29
|
+
visualFocus,
|
|
30
|
+
setVisualFocus: value => {
|
|
31
|
+
setVisualFocus(value);
|
|
32
|
+
}
|
|
33
|
+
};
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
exports.useFocus = useFocus;
|
|
37
|
+
//# sourceMappingURL=useFocus.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useFocus.js","sources":["../../src/moneyInputField/useFocus.ts"],"sourcesContent":["import { useEffect, useState } from 'react';\n\nconst focusTimeout = 5 * 1000;\n\nexport const useFocus = () => {\n const [focus, setFocus] = useState(false);\n const [visualFocus, setVisualFocus] = useState(false);\n const [counter, setCounter] = useState(0);\n\n useEffect(() => {\n if (focus) {\n const timeout = setTimeout(() => {\n setVisualFocus(false);\n }, focusTimeout);\n return () => clearTimeout(timeout);\n }\n }, [focus, counter]);\n\n return {\n focus,\n setFocus: (value: boolean) => {\n setFocus(value);\n if (value) {\n setVisualFocus(true);\n }\n // any call to setFocus should reset the timeout, even if the input was already in focus\n // updating the counter will trigger the useEffect to reset the timeout\n setCounter((prev) => prev + 1);\n },\n visualFocus,\n setVisualFocus: (value: boolean) => {\n setVisualFocus(value);\n },\n };\n};\n"],"names":["focusTimeout","useFocus","focus","setFocus","useState","visualFocus","setVisualFocus","counter","setCounter","useEffect","timeout","setTimeout","clearTimeout","value","prev"],"mappings":";;;;AAEA,MAAMA,YAAY,GAAG,CAAC,GAAG,IAAI;AAEtB,MAAMC,QAAQ,GAAGA,MAAK;EAC3B,MAAM,CAACC,KAAK,EAAEC,QAAQ,CAAC,GAAGC,cAAQ,CAAC,KAAK,CAAC;EACzC,MAAM,CAACC,WAAW,EAAEC,cAAc,CAAC,GAAGF,cAAQ,CAAC,KAAK,CAAC;EACrD,MAAM,CAACG,OAAO,EAAEC,UAAU,CAAC,GAAGJ,cAAQ,CAAC,CAAC,CAAC;AAEzCK,EAAAA,eAAS,CAAC,MAAK;AACb,IAAA,IAAIP,KAAK,EAAE;AACT,MAAA,MAAMQ,OAAO,GAAGC,UAAU,CAAC,MAAK;QAC9BL,cAAc,CAAC,KAAK,CAAC;MACvB,CAAC,EAAEN,YAAY,CAAC;AAChB,MAAA,OAAO,MAAMY,YAAY,CAACF,OAAO,CAAC;AACpC,IAAA;AACF,EAAA,CAAC,EAAE,CAACR,KAAK,EAAEK,OAAO,CAAC,CAAC;EAEpB,OAAO;IACLL,KAAK;IACLC,QAAQ,EAAGU,KAAc,IAAI;MAC3BV,QAAQ,CAACU,KAAK,CAAC;AACf,MAAA,IAAIA,KAAK,EAAE;QACTP,cAAc,CAAC,IAAI,CAAC;AACtB,MAAA;AACA;AACA;AACAE,MAAAA,UAAU,CAAEM,IAAI,IAAKA,IAAI,GAAG,CAAC,CAAC;IAChC,CAAC;IACDT,WAAW;IACXC,cAAc,EAAGO,KAAc,IAAI;MACjCP,cAAc,CAACO,KAAK,CAAC;AACvB,IAAA;GACD;AACH;;;;"}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { useState, useEffect } from 'react';
|
|
2
|
+
|
|
3
|
+
const focusTimeout = 5 * 1000;
|
|
4
|
+
const useFocus = () => {
|
|
5
|
+
const [focus, setFocus] = useState(false);
|
|
6
|
+
const [visualFocus, setVisualFocus] = useState(false);
|
|
7
|
+
const [counter, setCounter] = useState(0);
|
|
8
|
+
useEffect(() => {
|
|
9
|
+
if (focus) {
|
|
10
|
+
const timeout = setTimeout(() => {
|
|
11
|
+
setVisualFocus(false);
|
|
12
|
+
}, focusTimeout);
|
|
13
|
+
return () => clearTimeout(timeout);
|
|
14
|
+
}
|
|
15
|
+
}, [focus, counter]);
|
|
16
|
+
return {
|
|
17
|
+
focus,
|
|
18
|
+
setFocus: value => {
|
|
19
|
+
setFocus(value);
|
|
20
|
+
if (value) {
|
|
21
|
+
setVisualFocus(true);
|
|
22
|
+
}
|
|
23
|
+
// any call to setFocus should reset the timeout, even if the input was already in focus
|
|
24
|
+
// updating the counter will trigger the useEffect to reset the timeout
|
|
25
|
+
setCounter(prev => prev + 1);
|
|
26
|
+
},
|
|
27
|
+
visualFocus,
|
|
28
|
+
setVisualFocus: value => {
|
|
29
|
+
setVisualFocus(value);
|
|
30
|
+
}
|
|
31
|
+
};
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
export { useFocus };
|
|
35
|
+
//# sourceMappingURL=useFocus.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useFocus.mjs","sources":["../../src/moneyInputField/useFocus.ts"],"sourcesContent":["import { useEffect, useState } from 'react';\n\nconst focusTimeout = 5 * 1000;\n\nexport const useFocus = () => {\n const [focus, setFocus] = useState(false);\n const [visualFocus, setVisualFocus] = useState(false);\n const [counter, setCounter] = useState(0);\n\n useEffect(() => {\n if (focus) {\n const timeout = setTimeout(() => {\n setVisualFocus(false);\n }, focusTimeout);\n return () => clearTimeout(timeout);\n }\n }, [focus, counter]);\n\n return {\n focus,\n setFocus: (value: boolean) => {\n setFocus(value);\n if (value) {\n setVisualFocus(true);\n }\n // any call to setFocus should reset the timeout, even if the input was already in focus\n // updating the counter will trigger the useEffect to reset the timeout\n setCounter((prev) => prev + 1);\n },\n visualFocus,\n setVisualFocus: (value: boolean) => {\n setVisualFocus(value);\n },\n };\n};\n"],"names":["focusTimeout","useFocus","focus","setFocus","useState","visualFocus","setVisualFocus","counter","setCounter","useEffect","timeout","setTimeout","clearTimeout","value","prev"],"mappings":";;AAEA,MAAMA,YAAY,GAAG,CAAC,GAAG,IAAI;AAEtB,MAAMC,QAAQ,GAAGA,MAAK;EAC3B,MAAM,CAACC,KAAK,EAAEC,QAAQ,CAAC,GAAGC,QAAQ,CAAC,KAAK,CAAC;EACzC,MAAM,CAACC,WAAW,EAAEC,cAAc,CAAC,GAAGF,QAAQ,CAAC,KAAK,CAAC;EACrD,MAAM,CAACG,OAAO,EAAEC,UAAU,CAAC,GAAGJ,QAAQ,CAAC,CAAC,CAAC;AAEzCK,EAAAA,SAAS,CAAC,MAAK;AACb,IAAA,IAAIP,KAAK,EAAE;AACT,MAAA,MAAMQ,OAAO,GAAGC,UAAU,CAAC,MAAK;QAC9BL,cAAc,CAAC,KAAK,CAAC;MACvB,CAAC,EAAEN,YAAY,CAAC;AAChB,MAAA,OAAO,MAAMY,YAAY,CAACF,OAAO,CAAC;AACpC,IAAA;AACF,EAAA,CAAC,EAAE,CAACR,KAAK,EAAEK,OAAO,CAAC,CAAC;EAEpB,OAAO;IACLL,KAAK;IACLC,QAAQ,EAAGU,KAAc,IAAI;MAC3BV,QAAQ,CAACU,KAAK,CAAC;AACf,MAAA,IAAIA,KAAK,EAAE;QACTP,cAAc,CAAC,IAAI,CAAC;AACtB,MAAA;AACA;AACA;AACAE,MAAAA,UAAU,CAAEM,IAAI,IAAKA,IAAI,GAAG,CAAC,CAAC;IAChC,CAAC;IACDT,WAAW;IACXC,cAAc,EAAGO,KAAc,IAAI;MACjCP,cAAc,CAACO,KAAK,CAAC;AACvB,IAAA;GACD;AACH;;;;"}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var React = require('react');
|
|
4
|
+
|
|
5
|
+
const useInputStyle = ({
|
|
6
|
+
value,
|
|
7
|
+
focus,
|
|
8
|
+
inputElement,
|
|
9
|
+
loading
|
|
10
|
+
}) => {
|
|
11
|
+
const initialRender = !useTimeout(300);
|
|
12
|
+
const inputWidth = useFirstDefinedValue(inputElement?.clientWidth, [inputElement, value]);
|
|
13
|
+
const getStyle = () => {
|
|
14
|
+
const fontSize = getFontSize(value, focus, inputWidth);
|
|
15
|
+
return {
|
|
16
|
+
fontSize,
|
|
17
|
+
height: fontSize,
|
|
18
|
+
// aligns the top of the digit with the currency button
|
|
19
|
+
marginTop: fontSize * -0.19,
|
|
20
|
+
// if the input loads with a pre-filled value, we don't want to animate the font-size immediately
|
|
21
|
+
transition: initialRender ? 'none' : undefined,
|
|
22
|
+
color: loading ? 'var(--color-interactive-secondary)' : undefined
|
|
23
|
+
};
|
|
24
|
+
};
|
|
25
|
+
const [style, setStyle] = React.useState(getStyle());
|
|
26
|
+
React.useLayoutEffect(() => {
|
|
27
|
+
setStyle(getStyle());
|
|
28
|
+
}, [value, focus, loading, inputWidth]);
|
|
29
|
+
return style;
|
|
30
|
+
};
|
|
31
|
+
function getFontSize(inputValue, isFocused, inputWidth) {
|
|
32
|
+
const defaultFontSize = 52;
|
|
33
|
+
const focusFontSize = 90;
|
|
34
|
+
const minimumFontSize = 34;
|
|
35
|
+
let fontSize = isFocused ? focusFontSize : defaultFontSize;
|
|
36
|
+
if (typeof inputWidth === 'undefined') {
|
|
37
|
+
return fontSize;
|
|
38
|
+
}
|
|
39
|
+
const textLength = inputValue.length;
|
|
40
|
+
const maxCharactersWithoutShrinking = Math.floor(inputWidth / 40);
|
|
41
|
+
if (textLength > maxCharactersWithoutShrinking) {
|
|
42
|
+
const adjustedSize = Math.round(inputWidth / textLength * 1.9);
|
|
43
|
+
fontSize = Math.min(fontSize, adjustedSize);
|
|
44
|
+
}
|
|
45
|
+
return Math.max(fontSize, minimumFontSize);
|
|
46
|
+
}
|
|
47
|
+
const useFirstDefinedValue = (newValue, dependencies) => {
|
|
48
|
+
const [value, setValue] = React.useState(newValue);
|
|
49
|
+
React.useLayoutEffect(() => {
|
|
50
|
+
if (typeof newValue !== 'undefined' && typeof value === 'undefined') {
|
|
51
|
+
setValue(newValue);
|
|
52
|
+
}
|
|
53
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
54
|
+
}, [...dependencies, value]);
|
|
55
|
+
return value;
|
|
56
|
+
};
|
|
57
|
+
const useTimeout = delay => {
|
|
58
|
+
const [ready, setReady] = React.useState(false);
|
|
59
|
+
React.useEffect(() => {
|
|
60
|
+
const timeout = setTimeout(() => {
|
|
61
|
+
setReady(true);
|
|
62
|
+
}, delay);
|
|
63
|
+
return () => {
|
|
64
|
+
clearTimeout(timeout);
|
|
65
|
+
};
|
|
66
|
+
}, [delay]);
|
|
67
|
+
return ready;
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
exports.useInputStyle = useInputStyle;
|
|
71
|
+
//# sourceMappingURL=useInputStyle.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useInputStyle.js","sources":["../../src/moneyInputField/useInputStyle.ts"],"sourcesContent":["import { type CSSProperties, useEffect, useLayoutEffect, useState } from 'react';\nimport { Props as MoneyInputFieldProps } from './MoneyInputField';\n\ntype InputStyleObject = {\n value: string;\n focus: boolean;\n inputElement: HTMLInputElement | null;\n} & Pick<MoneyInputFieldProps, 'loading'>;\n\nexport const useInputStyle = ({ value, focus, inputElement, loading }: InputStyleObject) => {\n const initialRender = !useTimeout(300);\n const inputWidth = useFirstDefinedValue(inputElement?.clientWidth, [inputElement, value]);\n\n const getStyle = (): CSSProperties => {\n const fontSize = getFontSize(value, focus, inputWidth);\n\n return {\n fontSize,\n height: fontSize,\n // aligns the top of the digit with the currency button\n marginTop: fontSize * -0.19,\n // if the input loads with a pre-filled value, we don't want to animate the font-size immediately\n transition: initialRender ? 'none' : undefined,\n color: loading ? 'var(--color-interactive-secondary)' : undefined,\n };\n };\n\n const [style, setStyle] = useState(getStyle());\n\n useLayoutEffect(() => {\n setStyle(getStyle());\n }, [value, focus, loading, inputWidth]);\n\n return style;\n};\n\nfunction getFontSize(inputValue: string, isFocused: boolean, inputWidth: number | undefined) {\n const defaultFontSize = 52;\n const focusFontSize = 90;\n const minimumFontSize = 34;\n\n let fontSize = isFocused ? focusFontSize : defaultFontSize;\n\n if (typeof inputWidth === 'undefined') {\n return fontSize;\n }\n const textLength = inputValue.length;\n const maxCharactersWithoutShrinking = Math.floor(inputWidth / 40);\n\n if (textLength > maxCharactersWithoutShrinking) {\n const adjustedSize = Math.round((inputWidth / textLength) * 1.9);\n fontSize = Math.min(fontSize, adjustedSize);\n }\n\n return Math.max(fontSize, minimumFontSize);\n}\n\nconst useFirstDefinedValue = (newValue: number | undefined, dependencies: unknown[]) => {\n const [value, setValue] = useState<number | undefined>(newValue);\n\n useLayoutEffect(() => {\n if (typeof newValue !== 'undefined' && typeof value === 'undefined') {\n setValue(newValue);\n }\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [...dependencies, value]);\n\n return value;\n};\n\nconst useTimeout = (delay: number) => {\n const [ready, setReady] = useState(false);\n\n useEffect(() => {\n const timeout = setTimeout(() => {\n setReady(true);\n }, delay);\n\n return () => {\n clearTimeout(timeout);\n };\n }, [delay]);\n\n return ready;\n};\n"],"names":["useInputStyle","value","focus","inputElement","loading","initialRender","useTimeout","inputWidth","useFirstDefinedValue","clientWidth","getStyle","fontSize","getFontSize","height","marginTop","transition","undefined","color","style","setStyle","useState","useLayoutEffect","inputValue","isFocused","defaultFontSize","focusFontSize","minimumFontSize","textLength","length","maxCharactersWithoutShrinking","Math","floor","adjustedSize","round","min","max","newValue","dependencies","setValue","delay","ready","setReady","useEffect","timeout","setTimeout","clearTimeout"],"mappings":"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"}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import { useState, useLayoutEffect, useEffect } from 'react';
|
|
2
|
+
|
|
3
|
+
const useInputStyle = ({
|
|
4
|
+
value,
|
|
5
|
+
focus,
|
|
6
|
+
inputElement,
|
|
7
|
+
loading
|
|
8
|
+
}) => {
|
|
9
|
+
const initialRender = !useTimeout(300);
|
|
10
|
+
const inputWidth = useFirstDefinedValue(inputElement?.clientWidth, [inputElement, value]);
|
|
11
|
+
const getStyle = () => {
|
|
12
|
+
const fontSize = getFontSize(value, focus, inputWidth);
|
|
13
|
+
return {
|
|
14
|
+
fontSize,
|
|
15
|
+
height: fontSize,
|
|
16
|
+
// aligns the top of the digit with the currency button
|
|
17
|
+
marginTop: fontSize * -0.19,
|
|
18
|
+
// if the input loads with a pre-filled value, we don't want to animate the font-size immediately
|
|
19
|
+
transition: initialRender ? 'none' : undefined,
|
|
20
|
+
color: loading ? 'var(--color-interactive-secondary)' : undefined
|
|
21
|
+
};
|
|
22
|
+
};
|
|
23
|
+
const [style, setStyle] = useState(getStyle());
|
|
24
|
+
useLayoutEffect(() => {
|
|
25
|
+
setStyle(getStyle());
|
|
26
|
+
}, [value, focus, loading, inputWidth]);
|
|
27
|
+
return style;
|
|
28
|
+
};
|
|
29
|
+
function getFontSize(inputValue, isFocused, inputWidth) {
|
|
30
|
+
const defaultFontSize = 52;
|
|
31
|
+
const focusFontSize = 90;
|
|
32
|
+
const minimumFontSize = 34;
|
|
33
|
+
let fontSize = isFocused ? focusFontSize : defaultFontSize;
|
|
34
|
+
if (typeof inputWidth === 'undefined') {
|
|
35
|
+
return fontSize;
|
|
36
|
+
}
|
|
37
|
+
const textLength = inputValue.length;
|
|
38
|
+
const maxCharactersWithoutShrinking = Math.floor(inputWidth / 40);
|
|
39
|
+
if (textLength > maxCharactersWithoutShrinking) {
|
|
40
|
+
const adjustedSize = Math.round(inputWidth / textLength * 1.9);
|
|
41
|
+
fontSize = Math.min(fontSize, adjustedSize);
|
|
42
|
+
}
|
|
43
|
+
return Math.max(fontSize, minimumFontSize);
|
|
44
|
+
}
|
|
45
|
+
const useFirstDefinedValue = (newValue, dependencies) => {
|
|
46
|
+
const [value, setValue] = useState(newValue);
|
|
47
|
+
useLayoutEffect(() => {
|
|
48
|
+
if (typeof newValue !== 'undefined' && typeof value === 'undefined') {
|
|
49
|
+
setValue(newValue);
|
|
50
|
+
}
|
|
51
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
52
|
+
}, [...dependencies, value]);
|
|
53
|
+
return value;
|
|
54
|
+
};
|
|
55
|
+
const useTimeout = delay => {
|
|
56
|
+
const [ready, setReady] = useState(false);
|
|
57
|
+
useEffect(() => {
|
|
58
|
+
const timeout = setTimeout(() => {
|
|
59
|
+
setReady(true);
|
|
60
|
+
}, delay);
|
|
61
|
+
return () => {
|
|
62
|
+
clearTimeout(timeout);
|
|
63
|
+
};
|
|
64
|
+
}, [delay]);
|
|
65
|
+
return ready;
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
export { useInputStyle };
|
|
69
|
+
//# sourceMappingURL=useInputStyle.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useInputStyle.mjs","sources":["../../src/moneyInputField/useInputStyle.ts"],"sourcesContent":["import { type CSSProperties, useEffect, useLayoutEffect, useState } from 'react';\nimport { Props as MoneyInputFieldProps } from './MoneyInputField';\n\ntype InputStyleObject = {\n value: string;\n focus: boolean;\n inputElement: HTMLInputElement | null;\n} & Pick<MoneyInputFieldProps, 'loading'>;\n\nexport const useInputStyle = ({ value, focus, inputElement, loading }: InputStyleObject) => {\n const initialRender = !useTimeout(300);\n const inputWidth = useFirstDefinedValue(inputElement?.clientWidth, [inputElement, value]);\n\n const getStyle = (): CSSProperties => {\n const fontSize = getFontSize(value, focus, inputWidth);\n\n return {\n fontSize,\n height: fontSize,\n // aligns the top of the digit with the currency button\n marginTop: fontSize * -0.19,\n // if the input loads with a pre-filled value, we don't want to animate the font-size immediately\n transition: initialRender ? 'none' : undefined,\n color: loading ? 'var(--color-interactive-secondary)' : undefined,\n };\n };\n\n const [style, setStyle] = useState(getStyle());\n\n useLayoutEffect(() => {\n setStyle(getStyle());\n }, [value, focus, loading, inputWidth]);\n\n return style;\n};\n\nfunction getFontSize(inputValue: string, isFocused: boolean, inputWidth: number | undefined) {\n const defaultFontSize = 52;\n const focusFontSize = 90;\n const minimumFontSize = 34;\n\n let fontSize = isFocused ? focusFontSize : defaultFontSize;\n\n if (typeof inputWidth === 'undefined') {\n return fontSize;\n }\n const textLength = inputValue.length;\n const maxCharactersWithoutShrinking = Math.floor(inputWidth / 40);\n\n if (textLength > maxCharactersWithoutShrinking) {\n const adjustedSize = Math.round((inputWidth / textLength) * 1.9);\n fontSize = Math.min(fontSize, adjustedSize);\n }\n\n return Math.max(fontSize, minimumFontSize);\n}\n\nconst useFirstDefinedValue = (newValue: number | undefined, dependencies: unknown[]) => {\n const [value, setValue] = useState<number | undefined>(newValue);\n\n useLayoutEffect(() => {\n if (typeof newValue !== 'undefined' && typeof value === 'undefined') {\n setValue(newValue);\n }\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [...dependencies, value]);\n\n return value;\n};\n\nconst useTimeout = (delay: number) => {\n const [ready, setReady] = useState(false);\n\n useEffect(() => {\n const timeout = setTimeout(() => {\n setReady(true);\n }, delay);\n\n return () => {\n clearTimeout(timeout);\n };\n }, [delay]);\n\n return ready;\n};\n"],"names":["useInputStyle","value","focus","inputElement","loading","initialRender","useTimeout","inputWidth","useFirstDefinedValue","clientWidth","getStyle","fontSize","getFontSize","height","marginTop","transition","undefined","color","style","setStyle","useState","useLayoutEffect","inputValue","isFocused","defaultFontSize","focusFontSize","minimumFontSize","textLength","length","maxCharactersWithoutShrinking","Math","floor","adjustedSize","round","min","max","newValue","dependencies","setValue","delay","ready","setReady","useEffect","timeout","setTimeout","clearTimeout"],"mappings":"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"}
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var formatting = require('@transferwise/formatting');
|
|
4
|
+
|
|
5
|
+
const getDecimalSeparator = (currency, locale) => {
|
|
6
|
+
return formatting.formatAmount(1.1, currency, locale).replace(/\p{Number}/gu, '');
|
|
7
|
+
};
|
|
8
|
+
const getGroupSeparator = (currency, locale) => {
|
|
9
|
+
return formatting.formatAmount(10000000, currency, locale).replace(/\p{Number}/gu, '')[0];
|
|
10
|
+
};
|
|
11
|
+
const getDecimalCount = (currency, locale) => {
|
|
12
|
+
const decimalSeparator = getDecimalSeparator(currency, locale);
|
|
13
|
+
if (!decimalSeparator) {
|
|
14
|
+
return 0;
|
|
15
|
+
}
|
|
16
|
+
const parts = formatting.formatAmount(1.1, currency, locale).split(decimalSeparator);
|
|
17
|
+
return parts.length === 2 ? parts[1].length : 0;
|
|
18
|
+
};
|
|
19
|
+
const getEnteredDecimalsCount = (value, decimalSeparator) => {
|
|
20
|
+
return value.split(decimalSeparator)[1]?.length ?? 0;
|
|
21
|
+
};
|
|
22
|
+
const getUnformattedNumber = ({
|
|
23
|
+
value: formattedValue,
|
|
24
|
+
currency,
|
|
25
|
+
locale
|
|
26
|
+
}) => {
|
|
27
|
+
const groupSeparator = getGroupSeparator(currency, locale);
|
|
28
|
+
const decimalSeparator = getDecimalSeparator(currency, locale);
|
|
29
|
+
if (!formattedValue) {
|
|
30
|
+
return null;
|
|
31
|
+
}
|
|
32
|
+
// parseFloat can't handle thousands separators
|
|
33
|
+
const withoutGroupSeparator = groupSeparator ? formattedValue.replace(/ /gu, '').replace(new RegExp(`\\${groupSeparator}`, 'g'), '') : formattedValue;
|
|
34
|
+
// parseFloat can only handle . as decimal separator
|
|
35
|
+
const withNormalisedDecimalSeparator = decimalSeparator ? withoutGroupSeparator.replace(decimalSeparator, '.') : withoutGroupSeparator;
|
|
36
|
+
const parsedValue = Number.parseFloat(withNormalisedDecimalSeparator);
|
|
37
|
+
return parsedValue;
|
|
38
|
+
};
|
|
39
|
+
const getFormattedString = ({
|
|
40
|
+
value: unformattedValue,
|
|
41
|
+
currency,
|
|
42
|
+
locale,
|
|
43
|
+
alwaysShowDecimals = false
|
|
44
|
+
}) => {
|
|
45
|
+
const decimalSeparator = getDecimalSeparator(currency, locale);
|
|
46
|
+
// formatAmount rounds extra decimals, so 1.999 will become 2. Instead we will manually strip extra decimals so that it becomes 1.99 after formatting
|
|
47
|
+
const decimalCount = getDecimalCount(currency, locale);
|
|
48
|
+
const unformattedString = unformattedValue.toString();
|
|
49
|
+
const [integerPart, decimalPart] = decimalSeparator ? unformattedString.split(decimalSeparator) : [unformattedString, undefined];
|
|
50
|
+
const formattedDecimalPart = decimalPart ? decimalPart.slice(0, decimalCount) : '';
|
|
51
|
+
const sanitisedUnformattedValue = Number.parseFloat(`${integerPart}.${formattedDecimalPart}`);
|
|
52
|
+
return formatting.formatAmount(sanitisedUnformattedValue, currency, locale, {
|
|
53
|
+
alwaysShowDecimals
|
|
54
|
+
});
|
|
55
|
+
};
|
|
56
|
+
const isInputPossiblyOverflowing = ({
|
|
57
|
+
ref,
|
|
58
|
+
value
|
|
59
|
+
}) => {
|
|
60
|
+
const textLength = value.length;
|
|
61
|
+
const inputWidth = ref.current?.clientWidth;
|
|
62
|
+
if (!inputWidth || !textLength) {
|
|
63
|
+
return;
|
|
64
|
+
}
|
|
65
|
+
const maxCharactersWithoutOverflow = Math.floor(inputWidth / 19);
|
|
66
|
+
return textLength > maxCharactersWithoutOverflow;
|
|
67
|
+
};
|
|
68
|
+
const allowedInputKeys = new Set(['Backspace', 'Delete', ',', '.', 'ArrowLeft', 'ArrowRight', 'Enter', 'Tab']);
|
|
69
|
+
const isAllowedInputKey = e => {
|
|
70
|
+
const {
|
|
71
|
+
metaKey,
|
|
72
|
+
key,
|
|
73
|
+
ctrlKey
|
|
74
|
+
} = e;
|
|
75
|
+
const isNumberKey = !Number.isNaN(Number.parseInt(key, 10));
|
|
76
|
+
return isNumberKey || metaKey || ctrlKey || allowedInputKeys.has(key);
|
|
77
|
+
};
|
|
78
|
+
|
|
79
|
+
exports.getDecimalCount = getDecimalCount;
|
|
80
|
+
exports.getDecimalSeparator = getDecimalSeparator;
|
|
81
|
+
exports.getEnteredDecimalsCount = getEnteredDecimalsCount;
|
|
82
|
+
exports.getFormattedString = getFormattedString;
|
|
83
|
+
exports.getGroupSeparator = getGroupSeparator;
|
|
84
|
+
exports.getUnformattedNumber = getUnformattedNumber;
|
|
85
|
+
exports.isAllowedInputKey = isAllowedInputKey;
|
|
86
|
+
exports.isInputPossiblyOverflowing = isInputPossiblyOverflowing;
|
|
87
|
+
//# sourceMappingURL=utils.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"utils.js","sources":["../../src/moneyInputField/utils.ts"],"sourcesContent":["import { formatAmount } from '@transferwise/formatting';\nimport type { KeyboardEvent } from 'react';\n\nexport const getDecimalSeparator = (currency: string, locale: string): string | null => {\n return formatAmount(1.1, currency, locale).replace(/\\p{Number}/gu, '');\n};\n\nexport const getGroupSeparator = (currency: string, locale: string): string | null => {\n return formatAmount(10000000, currency, locale).replace(/\\p{Number}/gu, '')[0];\n};\n\nexport const getDecimalCount = (currency: string, locale: string): number => {\n const decimalSeparator = getDecimalSeparator(currency, locale);\n if (!decimalSeparator) {\n return 0;\n }\n const parts = formatAmount(1.1, currency, locale).split(decimalSeparator);\n return parts.length === 2 ? parts[1].length : 0;\n};\n\nexport const getEnteredDecimalsCount = (value: string, decimalSeparator: string) => {\n return value.split(decimalSeparator)[1]?.length ?? 0;\n};\n\nexport const getUnformattedNumber = ({\n value: formattedValue,\n currency,\n locale,\n}: {\n value: string;\n currency: string;\n locale: string;\n}): number | null => {\n const groupSeparator = getGroupSeparator(currency, locale);\n const decimalSeparator = getDecimalSeparator(currency, locale);\n if (!formattedValue) {\n return null;\n }\n\n // parseFloat can't handle thousands separators\n const withoutGroupSeparator = groupSeparator\n ? formattedValue.replace(/ /gu, '').replace(new RegExp(`\\\\${groupSeparator}`, 'g'), '')\n : formattedValue;\n\n // parseFloat can only handle . as decimal separator\n const withNormalisedDecimalSeparator = decimalSeparator\n ? withoutGroupSeparator.replace(decimalSeparator, '.')\n : withoutGroupSeparator;\n\n const parsedValue = Number.parseFloat(withNormalisedDecimalSeparator);\n return parsedValue;\n};\n\nexport const getFormattedString = ({\n value: unformattedValue,\n currency,\n locale,\n alwaysShowDecimals = false,\n}: {\n value: number;\n currency: string;\n locale: string;\n alwaysShowDecimals?: boolean;\n}): string => {\n const decimalSeparator = getDecimalSeparator(currency, locale);\n // formatAmount rounds extra decimals, so 1.999 will become 2. Instead we will manually strip extra decimals so that it becomes 1.99 after formatting\n const decimalCount = getDecimalCount(currency, locale);\n const unformattedString = unformattedValue.toString();\n\n const [integerPart, decimalPart] = decimalSeparator\n ? unformattedString.split(decimalSeparator)\n : [unformattedString, undefined];\n\n const formattedDecimalPart = decimalPart ? decimalPart.slice(0, decimalCount) : '';\n\n const sanitisedUnformattedValue = Number.parseFloat(`${integerPart}.${formattedDecimalPart}`);\n\n return formatAmount(sanitisedUnformattedValue, currency, locale, {\n alwaysShowDecimals,\n });\n};\n\nexport const isInputPossiblyOverflowing = ({\n ref,\n value,\n}: {\n ref: React.RefObject<HTMLInputElement>;\n value: string;\n}) => {\n const textLength = value.length;\n const inputWidth = ref.current?.clientWidth;\n if (!inputWidth || !textLength) {\n return;\n }\n\n const maxCharactersWithoutOverflow = Math.floor(inputWidth / 19);\n return textLength > maxCharactersWithoutOverflow;\n};\n\nconst allowedInputKeys = new Set([\n 'Backspace',\n 'Delete',\n ',',\n '.',\n 'ArrowLeft',\n 'ArrowRight',\n 'Enter',\n 'Tab',\n]);\n\nexport const isAllowedInputKey = (e: KeyboardEvent<HTMLInputElement>) => {\n const { metaKey, key, ctrlKey } = e;\n const isNumberKey = !Number.isNaN(Number.parseInt(key, 10));\n\n return isNumberKey || metaKey || ctrlKey || allowedInputKeys.has(key);\n};\n"],"names":["getDecimalSeparator","currency","locale","formatAmount","replace","getGroupSeparator","getDecimalCount","decimalSeparator","parts","split","length","getEnteredDecimalsCount","value","getUnformattedNumber","formattedValue","groupSeparator","withoutGroupSeparator","RegExp","withNormalisedDecimalSeparator","parsedValue","Number","parseFloat","getFormattedString","unformattedValue","alwaysShowDecimals","decimalCount","unformattedString","toString","integerPart","decimalPart","undefined","formattedDecimalPart","slice","sanitisedUnformattedValue","isInputPossiblyOverflowing","ref","textLength","inputWidth","current","clientWidth","maxCharactersWithoutOverflow","Math","floor","allowedInputKeys","Set","isAllowedInputKey","e","metaKey","key","ctrlKey","isNumberKey","isNaN","parseInt","has"],"mappings":"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"}
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import { formatAmount } from '@transferwise/formatting';
|
|
2
|
+
|
|
3
|
+
const getDecimalSeparator = (currency, locale) => {
|
|
4
|
+
return formatAmount(1.1, currency, locale).replace(/\p{Number}/gu, '');
|
|
5
|
+
};
|
|
6
|
+
const getGroupSeparator = (currency, locale) => {
|
|
7
|
+
return formatAmount(10000000, currency, locale).replace(/\p{Number}/gu, '')[0];
|
|
8
|
+
};
|
|
9
|
+
const getDecimalCount = (currency, locale) => {
|
|
10
|
+
const decimalSeparator = getDecimalSeparator(currency, locale);
|
|
11
|
+
if (!decimalSeparator) {
|
|
12
|
+
return 0;
|
|
13
|
+
}
|
|
14
|
+
const parts = formatAmount(1.1, currency, locale).split(decimalSeparator);
|
|
15
|
+
return parts.length === 2 ? parts[1].length : 0;
|
|
16
|
+
};
|
|
17
|
+
const getEnteredDecimalsCount = (value, decimalSeparator) => {
|
|
18
|
+
return value.split(decimalSeparator)[1]?.length ?? 0;
|
|
19
|
+
};
|
|
20
|
+
const getUnformattedNumber = ({
|
|
21
|
+
value: formattedValue,
|
|
22
|
+
currency,
|
|
23
|
+
locale
|
|
24
|
+
}) => {
|
|
25
|
+
const groupSeparator = getGroupSeparator(currency, locale);
|
|
26
|
+
const decimalSeparator = getDecimalSeparator(currency, locale);
|
|
27
|
+
if (!formattedValue) {
|
|
28
|
+
return null;
|
|
29
|
+
}
|
|
30
|
+
// parseFloat can't handle thousands separators
|
|
31
|
+
const withoutGroupSeparator = groupSeparator ? formattedValue.replace(/ /gu, '').replace(new RegExp(`\\${groupSeparator}`, 'g'), '') : formattedValue;
|
|
32
|
+
// parseFloat can only handle . as decimal separator
|
|
33
|
+
const withNormalisedDecimalSeparator = decimalSeparator ? withoutGroupSeparator.replace(decimalSeparator, '.') : withoutGroupSeparator;
|
|
34
|
+
const parsedValue = Number.parseFloat(withNormalisedDecimalSeparator);
|
|
35
|
+
return parsedValue;
|
|
36
|
+
};
|
|
37
|
+
const getFormattedString = ({
|
|
38
|
+
value: unformattedValue,
|
|
39
|
+
currency,
|
|
40
|
+
locale,
|
|
41
|
+
alwaysShowDecimals = false
|
|
42
|
+
}) => {
|
|
43
|
+
const decimalSeparator = getDecimalSeparator(currency, locale);
|
|
44
|
+
// formatAmount rounds extra decimals, so 1.999 will become 2. Instead we will manually strip extra decimals so that it becomes 1.99 after formatting
|
|
45
|
+
const decimalCount = getDecimalCount(currency, locale);
|
|
46
|
+
const unformattedString = unformattedValue.toString();
|
|
47
|
+
const [integerPart, decimalPart] = decimalSeparator ? unformattedString.split(decimalSeparator) : [unformattedString, undefined];
|
|
48
|
+
const formattedDecimalPart = decimalPart ? decimalPart.slice(0, decimalCount) : '';
|
|
49
|
+
const sanitisedUnformattedValue = Number.parseFloat(`${integerPart}.${formattedDecimalPart}`);
|
|
50
|
+
return formatAmount(sanitisedUnformattedValue, currency, locale, {
|
|
51
|
+
alwaysShowDecimals
|
|
52
|
+
});
|
|
53
|
+
};
|
|
54
|
+
const isInputPossiblyOverflowing = ({
|
|
55
|
+
ref,
|
|
56
|
+
value
|
|
57
|
+
}) => {
|
|
58
|
+
const textLength = value.length;
|
|
59
|
+
const inputWidth = ref.current?.clientWidth;
|
|
60
|
+
if (!inputWidth || !textLength) {
|
|
61
|
+
return;
|
|
62
|
+
}
|
|
63
|
+
const maxCharactersWithoutOverflow = Math.floor(inputWidth / 19);
|
|
64
|
+
return textLength > maxCharactersWithoutOverflow;
|
|
65
|
+
};
|
|
66
|
+
const allowedInputKeys = new Set(['Backspace', 'Delete', ',', '.', 'ArrowLeft', 'ArrowRight', 'Enter', 'Tab']);
|
|
67
|
+
const isAllowedInputKey = e => {
|
|
68
|
+
const {
|
|
69
|
+
metaKey,
|
|
70
|
+
key,
|
|
71
|
+
ctrlKey
|
|
72
|
+
} = e;
|
|
73
|
+
const isNumberKey = !Number.isNaN(Number.parseInt(key, 10));
|
|
74
|
+
return isNumberKey || metaKey || ctrlKey || allowedInputKeys.has(key);
|
|
75
|
+
};
|
|
76
|
+
|
|
77
|
+
export { getDecimalCount, getDecimalSeparator, getEnteredDecimalsCount, getFormattedString, getGroupSeparator, getUnformattedNumber, isAllowedInputKey, isInputPossiblyOverflowing };
|
|
78
|
+
//# sourceMappingURL=utils.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"utils.mjs","sources":["../../src/moneyInputField/utils.ts"],"sourcesContent":["import { formatAmount } from '@transferwise/formatting';\nimport type { KeyboardEvent } from 'react';\n\nexport const getDecimalSeparator = (currency: string, locale: string): string | null => {\n return formatAmount(1.1, currency, locale).replace(/\\p{Number}/gu, '');\n};\n\nexport const getGroupSeparator = (currency: string, locale: string): string | null => {\n return formatAmount(10000000, currency, locale).replace(/\\p{Number}/gu, '')[0];\n};\n\nexport const getDecimalCount = (currency: string, locale: string): number => {\n const decimalSeparator = getDecimalSeparator(currency, locale);\n if (!decimalSeparator) {\n return 0;\n }\n const parts = formatAmount(1.1, currency, locale).split(decimalSeparator);\n return parts.length === 2 ? parts[1].length : 0;\n};\n\nexport const getEnteredDecimalsCount = (value: string, decimalSeparator: string) => {\n return value.split(decimalSeparator)[1]?.length ?? 0;\n};\n\nexport const getUnformattedNumber = ({\n value: formattedValue,\n currency,\n locale,\n}: {\n value: string;\n currency: string;\n locale: string;\n}): number | null => {\n const groupSeparator = getGroupSeparator(currency, locale);\n const decimalSeparator = getDecimalSeparator(currency, locale);\n if (!formattedValue) {\n return null;\n }\n\n // parseFloat can't handle thousands separators\n const withoutGroupSeparator = groupSeparator\n ? formattedValue.replace(/ /gu, '').replace(new RegExp(`\\\\${groupSeparator}`, 'g'), '')\n : formattedValue;\n\n // parseFloat can only handle . as decimal separator\n const withNormalisedDecimalSeparator = decimalSeparator\n ? withoutGroupSeparator.replace(decimalSeparator, '.')\n : withoutGroupSeparator;\n\n const parsedValue = Number.parseFloat(withNormalisedDecimalSeparator);\n return parsedValue;\n};\n\nexport const getFormattedString = ({\n value: unformattedValue,\n currency,\n locale,\n alwaysShowDecimals = false,\n}: {\n value: number;\n currency: string;\n locale: string;\n alwaysShowDecimals?: boolean;\n}): string => {\n const decimalSeparator = getDecimalSeparator(currency, locale);\n // formatAmount rounds extra decimals, so 1.999 will become 2. Instead we will manually strip extra decimals so that it becomes 1.99 after formatting\n const decimalCount = getDecimalCount(currency, locale);\n const unformattedString = unformattedValue.toString();\n\n const [integerPart, decimalPart] = decimalSeparator\n ? unformattedString.split(decimalSeparator)\n : [unformattedString, undefined];\n\n const formattedDecimalPart = decimalPart ? decimalPart.slice(0, decimalCount) : '';\n\n const sanitisedUnformattedValue = Number.parseFloat(`${integerPart}.${formattedDecimalPart}`);\n\n return formatAmount(sanitisedUnformattedValue, currency, locale, {\n alwaysShowDecimals,\n });\n};\n\nexport const isInputPossiblyOverflowing = ({\n ref,\n value,\n}: {\n ref: React.RefObject<HTMLInputElement>;\n value: string;\n}) => {\n const textLength = value.length;\n const inputWidth = ref.current?.clientWidth;\n if (!inputWidth || !textLength) {\n return;\n }\n\n const maxCharactersWithoutOverflow = Math.floor(inputWidth / 19);\n return textLength > maxCharactersWithoutOverflow;\n};\n\nconst allowedInputKeys = new Set([\n 'Backspace',\n 'Delete',\n ',',\n '.',\n 'ArrowLeft',\n 'ArrowRight',\n 'Enter',\n 'Tab',\n]);\n\nexport const isAllowedInputKey = (e: KeyboardEvent<HTMLInputElement>) => {\n const { metaKey, key, ctrlKey } = e;\n const isNumberKey = !Number.isNaN(Number.parseInt(key, 10));\n\n return isNumberKey || metaKey || ctrlKey || allowedInputKeys.has(key);\n};\n"],"names":["getDecimalSeparator","currency","locale","formatAmount","replace","getGroupSeparator","getDecimalCount","decimalSeparator","parts","split","length","getEnteredDecimalsCount","value","getUnformattedNumber","formattedValue","groupSeparator","withoutGroupSeparator","RegExp","withNormalisedDecimalSeparator","parsedValue","Number","parseFloat","getFormattedString","unformattedValue","alwaysShowDecimals","decimalCount","unformattedString","toString","integerPart","decimalPart","undefined","formattedDecimalPart","slice","sanitisedUnformattedValue","isInputPossiblyOverflowing","ref","textLength","inputWidth","current","clientWidth","maxCharactersWithoutOverflow","Math","floor","allowedInputKeys","Set","isAllowedInputKey","e","metaKey","key","ctrlKey","isNumberKey","isNaN","parseInt","has"],"mappings":"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"}
|
package/build/styles/main.css
CHANGED
|
@@ -4447,6 +4447,64 @@ button.np-link {
|
|
|
4447
4447
|
box-shadow: inset 0 0 0 1px #c9cbce !important;
|
|
4448
4448
|
box-shadow: inset 0 0 0 1px var(--color-interactive-secondary) !important;
|
|
4449
4449
|
}
|
|
4450
|
+
.wds-amount-input-container {
|
|
4451
|
+
width: 100%;
|
|
4452
|
+
}
|
|
4453
|
+
.wds-amount-input-input-container {
|
|
4454
|
+
display: flex;
|
|
4455
|
+
justify-content: right;
|
|
4456
|
+
width: 100%;
|
|
4457
|
+
transition: font-size 0.4s cubic-bezier(0.3, 0, 0.1, 1), height 0.4s cubic-bezier(0.3, 0, 0.1, 1), margin-top 0.4s cubic-bezier(0.3, 0, 0.1, 1), color 0.4s ease;
|
|
4458
|
+
color: var(--color-interactive-primary);
|
|
4459
|
+
overflow: hidden;
|
|
4460
|
+
margin-bottom: 0 !important;
|
|
4461
|
+
}
|
|
4462
|
+
@media (prefers-reduced-motion: reduce) {
|
|
4463
|
+
.wds-amount-input-input-container {
|
|
4464
|
+
transition: none;
|
|
4465
|
+
}
|
|
4466
|
+
}
|
|
4467
|
+
.wds-amount-input-input {
|
|
4468
|
+
border: none;
|
|
4469
|
+
outline: none;
|
|
4470
|
+
flex-grow: 1;
|
|
4471
|
+
text-align: right;
|
|
4472
|
+
background-color: transparent;
|
|
4473
|
+
}
|
|
4474
|
+
.wds-amount-input-input:focus-visible {
|
|
4475
|
+
outline: none;
|
|
4476
|
+
}
|
|
4477
|
+
.wds-amount-input-placeholder {
|
|
4478
|
+
flex-grow: 0;
|
|
4479
|
+
display: flex;
|
|
4480
|
+
align-items: center;
|
|
4481
|
+
}
|
|
4482
|
+
.wds-currency-selector:disabled {
|
|
4483
|
+
opacity: 1 !important;
|
|
4484
|
+
cursor: auto !important;
|
|
4485
|
+
cursor: initial !important;
|
|
4486
|
+
mix-blend-mode: initial !important;
|
|
4487
|
+
}
|
|
4488
|
+
.wds-chevron-container {
|
|
4489
|
+
width: 32px;
|
|
4490
|
+
width: var(--size-32);
|
|
4491
|
+
overflow: hidden;
|
|
4492
|
+
color: var(--color-interactive-primary);
|
|
4493
|
+
margin-left: 8px;
|
|
4494
|
+
margin-left: var(--size-8);
|
|
4495
|
+
transition: width 0.3s ease;
|
|
4496
|
+
}
|
|
4497
|
+
.wds-chevron-hidden {
|
|
4498
|
+
width: 0;
|
|
4499
|
+
}
|
|
4500
|
+
.wds-money-input-field-currency-selector {
|
|
4501
|
+
flex-shrink: 0;
|
|
4502
|
+
margin-right: 24px;
|
|
4503
|
+
margin-right: var(--size-24);
|
|
4504
|
+
}
|
|
4505
|
+
.wds-money-input-field-chevron {
|
|
4506
|
+
transform: translateY(-5%);
|
|
4507
|
+
}
|
|
4450
4508
|
.np-navigation-option {
|
|
4451
4509
|
-webkit-text-decoration: none;
|
|
4452
4510
|
text-decoration: none;
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
.wds-amount-input-container {
|
|
2
|
+
width: 100%;
|
|
3
|
+
}
|
|
4
|
+
.wds-amount-input-input-container {
|
|
5
|
+
display: flex;
|
|
6
|
+
justify-content: right;
|
|
7
|
+
width: 100%;
|
|
8
|
+
transition: font-size 0.4s cubic-bezier(0.3, 0, 0.1, 1), height 0.4s cubic-bezier(0.3, 0, 0.1, 1), margin-top 0.4s cubic-bezier(0.3, 0, 0.1, 1), color 0.4s ease;
|
|
9
|
+
color: var(--color-interactive-primary);
|
|
10
|
+
overflow: hidden;
|
|
11
|
+
margin-bottom: 0 !important;
|
|
12
|
+
}
|
|
13
|
+
@media (prefers-reduced-motion: reduce) {
|
|
14
|
+
.wds-amount-input-input-container {
|
|
15
|
+
transition: none;
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
.wds-amount-input-input {
|
|
19
|
+
border: none;
|
|
20
|
+
outline: none;
|
|
21
|
+
flex-grow: 1;
|
|
22
|
+
text-align: right;
|
|
23
|
+
background-color: transparent;
|
|
24
|
+
}
|
|
25
|
+
.wds-amount-input-input:focus-visible {
|
|
26
|
+
outline: none;
|
|
27
|
+
}
|
|
28
|
+
.wds-amount-input-placeholder {
|
|
29
|
+
flex-grow: 0;
|
|
30
|
+
display: flex;
|
|
31
|
+
align-items: center;
|
|
32
|
+
}
|