fictoan-react 0.45.3 → 0.45.4
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/CHANGELOG.md +3 -0
- package/dist/cjs/components/PinInputField/PinInputField.js +1 -1
- package/dist/cjs/components/PinInputField/PinInputField.js.map +1 -1
- package/dist/es/components/PinInputField/PinInputField.js +1 -1
- package/dist/es/components/PinInputField/PinInputField.js.map +1 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
"use strict";Object.defineProperty(exports,"__esModule",{value:!0});var e=require("react"),t=require("../../external/Element.js"),r=require("../Form/InputField/InputField.js"),l=require("./PinInputField.styled.js");function n(e){return e&&"object"==typeof e&&"default"in e?e:{default:e}}require("../Form/BaseInputComponent/BaseInputComponent.js"),require("../Form/InputLabel/InputLabel.js"),require("../Form/FormItem/FormItem.js"),require("../Form/FormItem/FormItem.styled.js"),require("styled-components"),require("../Form/InputField/InputField.styled.js"),require("../Form/Select/Select.styled.js"),require("../Form/TextArea/TextArea.styled.js"),require("../../external/theme.js"),require("../../external/DefaultColours.js");var u=/*#__PURE__*/n(e);function a(e,t){return("alphanumeric"===t?/^[a-zA-Z0-9]+$/i:/^[0-9]+$/).test(e)}const s=u.default.forwardRef((({numberOfFields:n,onChange:s,type:o="number",mask:i=!1,otp:c=!1,autoFocus:p=!1,pasteFromClipboard:d="enabled",spacing:m="small"},f)=>{const[F,
|
|
1
|
+
"use strict";Object.defineProperty(exports,"__esModule",{value:!0});var e=require("react"),t=require("../../external/Element.js"),r=require("../Form/InputField/InputField.js"),l=require("./PinInputField.styled.js");function n(e){return e&&"object"==typeof e&&"default"in e?e:{default:e}}require("../Form/BaseInputComponent/BaseInputComponent.js"),require("../Form/InputLabel/InputLabel.js"),require("../Form/FormItem/FormItem.js"),require("../Form/FormItem/FormItem.styled.js"),require("styled-components"),require("../Form/InputField/InputField.styled.js"),require("../Form/Select/Select.styled.js"),require("../Form/TextArea/TextArea.styled.js"),require("../../external/theme.js"),require("../../external/DefaultColours.js");var u=/*#__PURE__*/n(e);function a(e,t){return("alphanumeric"===t?/^[a-zA-Z0-9]+$/i:/^[0-9]+$/).test(e)}const s=u.default.forwardRef((({numberOfFields:n,onChange:s,type:o="number",mask:i=!1,otp:c=!1,autoFocus:p=!1,pasteFromClipboard:d="enabled",spacing:m="small"},f)=>{const[F,h]=e.useState([]),[y,g]=e.useState([]),[v,b]=e.useState(!0),[j,I]=e.useState(-1),q=e.useCallback((e=>{var t;const r=F[e];null===(t=null==r?void 0:r.current)||void 0===t||t.focus()}),[F]);e.useEffect((()=>{h((t=>Array(n).fill(0).map(((r,l)=>{var n;const u=t[l]||e.createRef();return p&&0===l&&(null===(n=u.current)||void 0===n||n.focus()),u}))))}),[n,p]);const A=e.useCallback((e=>{if(!v)return;const t=e+1<n?e+1:null;t&&q(t)}),[q,n,v]),C=e.useCallback(((e,t)=>{const r=[...y];r[t]=e,g(r),null==s||s(r.join(""));""!==e&&r.length===n&&r.every((e=>null!=e&&""!==e))&&t==n-1||(b(!0),A(t))}),[A,n,s,y]),k=()=>{I(-1)};let x=[];return m&&x.push(`spacing-${m}`),u.default.createElement(t.Element,{as:l.PinInputStyled,classNames:x,ref:f},[...Array(n)].map(((e,t)=>u.default.createElement(r.InputField,{key:t,className:"pin-input-field",ref:F[t],type:i?"password":"number"===o?"tel":"text",inputMode:"number"===o?"numeric":"text",onChange:e=>((e,t)=>{const r=e.currentTarget.value,l=y[t];if(""!==r)if(r.length>1&&t<n-1){if(a(r,o)){let u=[];u=""==l?r.split("").filter(((e,r)=>t+r<n)):e.currentTarget.selectionEnd===r.length?r.split("").filter(((e,r)=>r>0&&t+r-1<n)):r.split("").filter(((e,l)=>l<r.length-1&&t+l<n)),g((e=>e.map(((e,r)=>r>=t&&r<t+u.length?u[r-t]:e)))),q(t+u.length<n?t+u.length:n-1),null==s||s(u.join(""))}}else{let e=r;(null==l?void 0:l.length)>0&&(l[0]===r.charAt(0)?e=r.charAt(1):l[0]===r.charAt(1)&&(e=r.charAt(0))),a(e,o)&&C(e,t)}else C("",t)})(e,t),onKeyDown:e=>((e,t)=>{var r;if("Backspace"===e.key)if(""===e.target.value){if(t>0){const e=t-1;C("",e),q(e),b(!0)}}else b(!1);else"Escape"===e.key?(null===(r=F[t].current)||void 0===r||r.blur(),k()):"ArrowRight"===e.key?t<n-1&&q(t+1):"ArrowLeft"===e.key&&t>0&&q(t-1)})(e,t),onFocus:e=>((e,t)=>{I(t)})(0,t),onSelect:e=>(e=>{const t=e.target;setTimeout((()=>{t.setSelectionRange(t.value.length,t.value.length)}),0)})(e),onBlur:k,placeholder:j!==t?"⦁":void 0,autoComplete:c?"one-time-code":"off",value:y[t]||"",autoFocus:p&&0===t,onCopy:e=>"disabled"===d&&e.preventDefault(),onPaste:e=>"disabled"===d&&e.preventDefault()}))))}));exports.PinInputField=s;
|
|
2
2
|
//# sourceMappingURL=PinInputField.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"PinInputField.js","sources":["../../../../src/components/PinInputField/PinInputField.tsx"],"sourcesContent":["import React, { createRef, useCallback, useEffect, useState } from \"react\";\n\nimport { Element } from \"../Element/Element\";\nimport { CommonAndHTMLProps, SpacingTypes } from \"../Element/constants\";\nimport { InputField } from \"../Form/InputField/InputField\";\n\nimport { PinInputStyled } from \"./PinInputField.styled\";\n\n// prettier-ignore\ntype PinInputFieldCustomProps = {\n numberOfFields : number;\n onChange ? : (value : string) => void;\n type ? : \"alphanumeric\" | \"number\";\n mask ? : boolean;\n otp ? : boolean;\n autoFocus ? : boolean;\n pasteFromClipboard ? : \"enabled\" | \"disabled\";\n spacing ? : SpacingTypes;\n};\n\nexport type PinInputFieldElementType = HTMLDivElement;\nexport type PinInputFieldProps = Omit<CommonAndHTMLProps<PinInputFieldElementType>, keyof PinInputFieldCustomProps> &\n PinInputFieldCustomProps;\n\nfunction validate(value: string, type: \"alphanumeric\" | \"number\") {\n const NUMERIC_REGEX = /^[0-9]+$/;\n const ALPHA_NUMERIC_REGEX = /^[a-zA-Z0-9]+$/i;\n const regex = type === \"alphanumeric\" ? ALPHA_NUMERIC_REGEX : NUMERIC_REGEX;\n return regex.test(value);\n}\n\nexport const PinInputField = React.forwardRef(\n (\n {\n numberOfFields: length,\n onChange,\n type = \"number\",\n mask = false,\n otp = false,\n autoFocus = false,\n pasteFromClipboard = \"enabled\",\n spacing = \"small\",\n }: PinInputFieldProps,\n ref: React.Ref<PinInputFieldElementType>\n ) => {\n const [inputRefs, setInputRefs] = useState<React.RefObject<HTMLInputElement>[]>([]);\n const [values, setValues] = useState<string[]>([]);\n const [moveFocus, setMoveFocus] = useState<boolean>(true);\n const [focusedIndex, setFocusedIndex] = useState<number>(-1);\n\n const focus = useCallback(\n (index: number) => {\n const ref = inputRefs[index];\n ref?.current?.focus();\n },\n [inputRefs]\n );\n\n useEffect(() => {\n setInputRefs((inputRefs) => {\n const refs = Array(length)\n .fill(0)\n .map((_, i) => {\n const ref = inputRefs[i] || createRef();\n if (autoFocus && i === 0) {\n ref.current?.focus();\n }\n return ref;\n });\n return refs;\n });\n }, [length, autoFocus]);\n\n const focusNext = useCallback(\n (index: number) => {\n if (!moveFocus) return;\n const next = index + 1 < length ? index + 1 : null;\n if (next) {\n focus(next);\n }\n },\n [focus, length, moveFocus]\n );\n\n const setValue = useCallback(\n (value: string, index: number) => {\n const nextValues = [...values];\n nextValues[index] = value;\n setValues(nextValues);\n onChange?.(nextValues.join(\"\"));\n\n const isComplete =\n value !== \"\" &&\n nextValues.length === length &&\n nextValues.every((inputValue) => inputValue != null && inputValue !== \"\");\n\n if (!isComplete) {\n focusNext(index);\n }\n },\n [focusNext, length, onChange, values]\n );\n\n const getNextValue = useCallback((value: string, eventValue: string) => {\n let nextValue = eventValue;\n if (value?.length > 0) {\n if (value[0] === eventValue.charAt(0)) {\n nextValue = eventValue.charAt(1);\n } else if (value[0] === eventValue.charAt(1)) {\n nextValue = eventValue.charAt(0);\n }\n }\n return nextValue;\n }, []);\n\n const handleInputChange = (event: React.FormEvent<HTMLInputElement>, i: number) => {\n const eventValue = event.currentTarget.value;\n const currentValue = values[i];\n const nextValue = getNextValue(currentValue, eventValue);\n\n if (nextValue === \"\") {\n setValue(\"\", i);\n return;\n }\n\n if (eventValue.length > 1 && i < length - 1) {\n if (validate(eventValue, type)) {\n const nextValue = eventValue.split(\"\").filter((_, index) => index < length);\n setValues(nextValue);\n focus(i + nextValue.length < length ? i + nextValue.length : length - 1);\n onChange?.(nextValue.join(\"\"));\n }\n } else {\n if (validate(nextValue, type)) {\n setValue(nextValue, i);\n }\n setMoveFocus(true);\n }\n };\n\n const onKeyDown = (event: React.KeyboardEvent, i: number) => {\n if (event.key === \"Backspace\") {\n if ((event.target as HTMLInputElement).value === \"\") {\n if (i > 0) {\n const newIndex = i - 1;\n setValue(\"\", newIndex);\n focus(newIndex);\n setMoveFocus(true);\n }\n } else {\n setMoveFocus(false);\n }\n } else if (event.key === \"Escape\") {\n inputRefs[i].current?.blur();\n onBlur();\n } else if (event.key === \"ArrowRight\") {\n if (i < length - 1) {\n focus(i + 1);\n }\n } else if (event.key === \"ArrowLeft\") {\n if (i > 0) {\n focus(i - 1);\n }\n }\n };\n\n const onFocus = (e: React.FocusEvent<HTMLInputElement>, i: number) => {\n setFocusedIndex(i);\n setTimeout(() => {\n // https://github.com/facebook/react/issues/6483\n e.target.setSelectionRange(e.target.value.length, e.target.value.length);\n }, 0);\n };\n\n const onBlur = () => {\n setFocusedIndex(-1);\n };\n\n let classNames = [];\n\n if (spacing) {\n classNames.push(`spacing-${spacing}`);\n }\n\n return (\n <Element<PinInputFieldElementType> as={PinInputStyled} classNames={classNames} ref={ref}>\n {[...Array(length)].map((_, i) => (\n <InputField\n key={i}\n className=\"pin-input-field\"\n ref={inputRefs[i]}\n type={mask ? \"password\" : type === \"number\" ? \"tel\" : \"text\"}\n inputMode={type === \"number\" ? \"numeric\" : \"text\"}\n onChange={(e) => handleInputChange(e, i)}\n onKeyDown={(e) => onKeyDown(e, i)}\n onFocus={(e) => onFocus(e, i)}\n onBlur={onBlur}\n placeholder={focusedIndex !== i ? `\\u2981` : undefined}\n autoComplete={otp ? \"one-time-code\" : \"off\"}\n value={values[i] || \"\"}\n autoFocus={autoFocus && i === 0}\n onCopy={e=> pasteFromClipboard === \"disabled\" && e.preventDefault()}\n onPaste={e=> pasteFromClipboard === \"disabled\" && e.preventDefault()}\n />\n ))}\n </Element>\n );\n }\n);\n"],"names":["validate","value","type","test","PinInputField","React","forwardRef","numberOfFields","length","onChange","mask","otp","autoFocus","pasteFromClipboard","spacing","ref","inputRefs","setInputRefs","useState","values","setValues","moveFocus","setMoveFocus","focusedIndex","setFocusedIndex","focus","useCallback","index","current","useEffect","Array","fill","map","_","i","createRef","focusNext","next","setValue","nextValues","join","every","inputValue","getNextValue","eventValue","nextValue","charAt","onBlur","classNames","push","Element","as","PinInputStyled","InputField","key","className","inputMode","e","event","currentTarget","currentValue","split","filter","handleInputChange","onKeyDown","target","newIndex","blur","onFocus","setTimeout","setSelectionRange","placeholder","undefined","autoComplete","onCopy","preventDefault","onPaste"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"PinInputField.js","sources":["../../../../src/components/PinInputField/PinInputField.tsx"],"sourcesContent":["import React, { createRef, useCallback, useEffect, useState } from \"react\";\n\nimport { Element } from \"../Element/Element\";\nimport { CommonAndHTMLProps, SpacingTypes } from \"../Element/constants\";\nimport { InputField } from \"../Form/InputField/InputField\";\n\nimport { PinInputStyled } from \"./PinInputField.styled\";\n\n// prettier-ignore\ntype PinInputFieldCustomProps = {\n numberOfFields : number;\n onChange ? : (value : string) => void;\n type ? : \"alphanumeric\" | \"number\";\n mask ? : boolean;\n otp ? : boolean;\n autoFocus ? : boolean;\n pasteFromClipboard ? : \"enabled\" | \"disabled\";\n spacing ? : SpacingTypes;\n};\n\nexport type PinInputFieldElementType = HTMLDivElement;\nexport type PinInputFieldProps = Omit<CommonAndHTMLProps<PinInputFieldElementType>, keyof PinInputFieldCustomProps> &\n PinInputFieldCustomProps;\n\nfunction validate(value: string, type: \"alphanumeric\" | \"number\") {\n const NUMERIC_REGEX = /^[0-9]+$/;\n const ALPHA_NUMERIC_REGEX = /^[a-zA-Z0-9]+$/i;\n const regex = type === \"alphanumeric\" ? ALPHA_NUMERIC_REGEX : NUMERIC_REGEX;\n return regex.test(value);\n}\n\nexport const PinInputField = React.forwardRef(\n (\n {\n numberOfFields: length,\n onChange,\n type = \"number\",\n mask = false,\n otp = false,\n autoFocus = false,\n pasteFromClipboard = \"enabled\",\n spacing = \"small\",\n }: PinInputFieldProps,\n ref: React.Ref<PinInputFieldElementType>\n ) => {\n const [inputRefs, setInputRefs] = useState<React.RefObject<HTMLInputElement>[]>([]);\n const [values, setValues] = useState<string[]>([]);\n const [moveFocus, setMoveFocus] = useState<boolean>(true);\n const [focusedIndex, setFocusedIndex] = useState<number>(-1);\n\n const focus = useCallback(\n (index: number) => {\n const ref = inputRefs[index];\n ref?.current?.focus();\n },\n [inputRefs]\n );\n\n useEffect(() => {\n setInputRefs((inputRefs) => {\n const refs = Array(length)\n .fill(0)\n .map((_, i) => {\n const ref = inputRefs[i] || createRef();\n if (autoFocus && i === 0) {\n ref.current?.focus();\n }\n return ref;\n });\n return refs;\n });\n }, [length, autoFocus]);\n\n const focusNext = useCallback(\n (index: number) => {\n if (!moveFocus) return;\n const next = index + 1 < length ? index + 1 : null;\n if (next) {\n focus(next);\n }\n },\n [focus, length, moveFocus]\n );\n\n const setValue = useCallback(\n (value: string, index: number) => {\n const nextValues = [...values];\n nextValues[index] = value;\n setValues(nextValues);\n onChange?.(nextValues.join(\"\"));\n\n const isComplete =\n value !== \"\" &&\n nextValues.length === length &&\n nextValues.every((inputValue) => inputValue != null && inputValue !== \"\") &&\n index == length - 1;\n\n if (!isComplete) {\n setMoveFocus(true);\n focusNext(index);\n }\n },\n [focusNext, length, onChange, values]\n );\n\n const handleInputChange = (event: React.FormEvent<HTMLInputElement>, inputFieldIndex: number) => {\n const eventValue = event.currentTarget.value;\n const currentValue = values[inputFieldIndex];\n\n if (eventValue === \"\") {\n setValue(\"\", inputFieldIndex);\n return;\n }\n\n // Handle scenario where multiple characters are entered in a single InputField\n if (eventValue.length > 1 && inputFieldIndex < length - 1) {\n if (validate(eventValue, type)) {\n let nextValue: string[] = [];\n // In all cases, we need to ensure characters longer than the remaining fields are removed.\n if (currentValue == \"\") {\n // Case: Current input field is empty\n nextValue = eventValue.split(\"\").filter((_, j) => inputFieldIndex + j < length);\n } else if (event.currentTarget.selectionEnd === eventValue.length) {\n // Case: Current field has a value and cursor is after it\n nextValue = eventValue.split(\"\").filter((_, j) => j > 0 && inputFieldIndex + j - 1 < length);\n } else {\n // Case: Current field has a value and cursor is before it\n nextValue = eventValue\n .split(\"\")\n .filter((_, j) => j < eventValue.length - 1 && inputFieldIndex + j < length);\n }\n setValues((values) =>\n values.map((v, j) =>\n j >= inputFieldIndex && j < inputFieldIndex + nextValue.length\n ? nextValue[j - inputFieldIndex]\n : v\n )\n );\n focus(\n inputFieldIndex + nextValue.length < length ? inputFieldIndex + nextValue.length : length - 1\n );\n onChange?.(nextValue.join(\"\"));\n }\n } else {\n let nextValue = eventValue;\n if (currentValue?.length > 0) {\n if (currentValue[0] === eventValue.charAt(0)) {\n nextValue = eventValue.charAt(1);\n } else if (currentValue[0] === eventValue.charAt(1)) {\n nextValue = eventValue.charAt(0);\n }\n }\n if (validate(nextValue, type)) {\n setValue(nextValue, inputFieldIndex);\n }\n }\n };\n\n const onKeyDown = (event: React.KeyboardEvent, i: number) => {\n if (event.key === \"Backspace\") {\n if ((event.target as HTMLInputElement).value === \"\") {\n if (i > 0) {\n const newIndex = i - 1;\n setValue(\"\", newIndex);\n focus(newIndex);\n setMoveFocus(true);\n }\n } else {\n setMoveFocus(false);\n }\n } else if (event.key === \"Escape\") {\n inputRefs[i].current?.blur();\n onBlur();\n } else if (event.key === \"ArrowRight\") {\n if (i < length - 1) {\n focus(i + 1);\n }\n } else if (event.key === \"ArrowLeft\") {\n if (i > 0) {\n focus(i - 1);\n }\n }\n };\n\n const onFocus = (e: React.FocusEvent<HTMLInputElement>, i: number) => {\n setFocusedIndex(i);\n };\n\n // When moving around the InputElements using tab key, browsers automatically select\n // the value (if it exists) in the InputElement - which we want to disable. Additionally,\n // when an existing value is selected/highlighted and pasted over, there is no way to\n // clearly distinguish between the other 2 scenarios of pasting by keeping the cursor before\n // and after the existing value. Specific example: If the existing value is 5, the event\n // when highlighting and pasting '567' is the same as placing the cursor before the existing\n // value and pasting '67'. By disabling this, we eliminate one of these cases.\n // Is this a hack? Yes. Is there a better way? IDK. Does it matter? Not unless there is a\n // valid reason for users to need selecting a single InputElement within a PinInput.\n const onSelect = (e: React.SyntheticEvent<HTMLInputElement, Event>) => {\n const target = e.target as HTMLInputElement;\n setTimeout(() => {\n // https://github.com/facebook/react/issues/6483\n target.setSelectionRange(target.value.length, target.value.length);\n }, 0);\n };\n\n const onBlur = () => {\n setFocusedIndex(-1);\n };\n\n let classNames = [];\n\n if (spacing) {\n classNames.push(`spacing-${spacing}`);\n }\n\n return (\n <Element<PinInputFieldElementType> as={PinInputStyled} classNames={classNames} ref={ref}>\n {[...Array(length)].map((_, i) => (\n <InputField\n key={i}\n className=\"pin-input-field\"\n ref={inputRefs[i]}\n type={mask ? \"password\" : type === \"number\" ? \"tel\" : \"text\"}\n inputMode={type === \"number\" ? \"numeric\" : \"text\"}\n onChange={(e) => handleInputChange(e, i)}\n onKeyDown={(e) => onKeyDown(e, i)}\n onFocus={(e) => onFocus(e, i)}\n onSelect={(e) => onSelect(e)}\n onBlur={onBlur}\n placeholder={focusedIndex !== i ? `\\u2981` : undefined}\n autoComplete={otp ? \"one-time-code\" : \"off\"}\n value={values[i] || \"\"}\n autoFocus={autoFocus && i === 0}\n onCopy={(e) => pasteFromClipboard === \"disabled\" && e.preventDefault()}\n onPaste={(e) => pasteFromClipboard === \"disabled\" && e.preventDefault()}\n />\n ))}\n </Element>\n );\n }\n);\n"],"names":["validate","value","type","test","PinInputField","React","forwardRef","numberOfFields","length","onChange","mask","otp","autoFocus","pasteFromClipboard","spacing","ref","inputRefs","setInputRefs","useState","values","setValues","moveFocus","setMoveFocus","focusedIndex","setFocusedIndex","focus","useCallback","index","current","useEffect","Array","fill","map","_","i","createRef","focusNext","next","setValue","nextValues","join","every","inputValue","onBlur","classNames","push","Element","as","PinInputStyled","InputField","key","className","inputMode","e","event","inputFieldIndex","eventValue","currentTarget","currentValue","nextValue","split","filter","j","selectionEnd","v","charAt","handleInputChange","onKeyDown","target","newIndex","blur","onFocus","onSelect","setTimeout","setSelectionRange","placeholder","undefined","autoComplete","onCopy","preventDefault","onPaste"],"mappings":"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"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import e,{useState as t,useCallback as r,useEffect as
|
|
1
|
+
import e,{useState as t,useCallback as r,useEffect as n,createRef as l}from"react";import{E as o}from"../../external/Element.js";import{InputField as s}from"../Form/InputField/InputField.js";import{PinInputStyled as a}from"./PinInputField.styled.js";import"../Form/BaseInputComponent/BaseInputComponent.js";import"../Form/InputLabel/InputLabel.js";import"../Form/FormItem/FormItem.js";import"../Form/FormItem/FormItem.styled.js";import"styled-components";import"../Form/InputField/InputField.styled.js";import"../Form/Select/Select.styled.js";import"../Form/TextArea/TextArea.styled.js";import"../../external/theme.js";import"../../external/DefaultColours.js";function i(e,t){return("alphanumeric"===t?/^[a-zA-Z0-9]+$/i:/^[0-9]+$/).test(e)}const m=/*#__PURE__*/e.forwardRef((({numberOfFields:m,onChange:u,type:p="number",mask:c=!1,otp:d=!1,autoFocus:f=!1,pasteFromClipboard:F="enabled",spacing:h="small"},g)=>{const[y,v]=t([]),[j,I]=t([]),[b,A]=t(!0),[x,C]=t(-1),k=r((e=>{var t;const r=y[e];null===(t=null==r?void 0:r.current)||void 0===t||t.focus()}),[y]);n((()=>{v((e=>Array(m).fill(0).map(((t,r)=>{var n;const o=e[r]||/*#__PURE__*/l();return f&&0===r&&(null===(n=o.current)||void 0===n||n.focus()),o}))))}),[m,f]);const E=r((e=>{if(!b)return;const t=e+1<m?e+1:null;t&&k(t)}),[k,m,b]),w=r(((e,t)=>{const r=[...j];r[t]=e,I(r),null==u||u(r.join(""));""!==e&&r.length===m&&r.every((e=>null!=e&&""!==e))&&t==m-1||(A(!0),E(t))}),[E,m,u,j]),T=()=>{C(-1)};let B=[];return h&&B.push(`spacing-${h}`),/*#__PURE__*/e.createElement(o,{as:a,classNames:B,ref:g},[...Array(m)].map(((t,r)=>/*#__PURE__*/e.createElement(s,{key:r,className:"pin-input-field",ref:y[r],type:c?"password":"number"===p?"tel":"text",inputMode:"number"===p?"numeric":"text",onChange:e=>((e,t)=>{const r=e.currentTarget.value,n=j[t];if(""!==r)if(r.length>1&&t<m-1){if(i(r,p)){let l=[];l=""==n?r.split("").filter(((e,r)=>t+r<m)):e.currentTarget.selectionEnd===r.length?r.split("").filter(((e,r)=>r>0&&t+r-1<m)):r.split("").filter(((e,n)=>n<r.length-1&&t+n<m)),I((e=>e.map(((e,r)=>r>=t&&r<t+l.length?l[r-t]:e)))),k(t+l.length<m?t+l.length:m-1),null==u||u(l.join(""))}}else{let e=r;(null==n?void 0:n.length)>0&&(n[0]===r.charAt(0)?e=r.charAt(1):n[0]===r.charAt(1)&&(e=r.charAt(0))),i(e,p)&&w(e,t)}else w("",t)})(e,r),onKeyDown:e=>((e,t)=>{var r;if("Backspace"===e.key)if(""===e.target.value){if(t>0){const e=t-1;w("",e),k(e),A(!0)}}else A(!1);else"Escape"===e.key?(null===(r=y[t].current)||void 0===r||r.blur(),T()):"ArrowRight"===e.key?t<m-1&&k(t+1):"ArrowLeft"===e.key&&t>0&&k(t-1)})(e,r),onFocus:e=>((e,t)=>{C(t)})(0,r),onSelect:e=>(e=>{const t=e.target;setTimeout((()=>{t.setSelectionRange(t.value.length,t.value.length)}),0)})(e),onBlur:T,placeholder:x!==r?"⦁":void 0,autoComplete:d?"one-time-code":"off",value:j[r]||"",autoFocus:f&&0===r,onCopy:e=>"disabled"===F&&e.preventDefault(),onPaste:e=>"disabled"===F&&e.preventDefault()}))))}));export{m as PinInputField};
|
|
2
2
|
//# sourceMappingURL=PinInputField.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"PinInputField.js","sources":["../../../../src/components/PinInputField/PinInputField.tsx"],"sourcesContent":["import React, { createRef, useCallback, useEffect, useState } from \"react\";\n\nimport { Element } from \"../Element/Element\";\nimport { CommonAndHTMLProps, SpacingTypes } from \"../Element/constants\";\nimport { InputField } from \"../Form/InputField/InputField\";\n\nimport { PinInputStyled } from \"./PinInputField.styled\";\n\n// prettier-ignore\ntype PinInputFieldCustomProps = {\n numberOfFields : number;\n onChange ? : (value : string) => void;\n type ? : \"alphanumeric\" | \"number\";\n mask ? : boolean;\n otp ? : boolean;\n autoFocus ? : boolean;\n pasteFromClipboard ? : \"enabled\" | \"disabled\";\n spacing ? : SpacingTypes;\n};\n\nexport type PinInputFieldElementType = HTMLDivElement;\nexport type PinInputFieldProps = Omit<CommonAndHTMLProps<PinInputFieldElementType>, keyof PinInputFieldCustomProps> &\n PinInputFieldCustomProps;\n\nfunction validate(value: string, type: \"alphanumeric\" | \"number\") {\n const NUMERIC_REGEX = /^[0-9]+$/;\n const ALPHA_NUMERIC_REGEX = /^[a-zA-Z0-9]+$/i;\n const regex = type === \"alphanumeric\" ? ALPHA_NUMERIC_REGEX : NUMERIC_REGEX;\n return regex.test(value);\n}\n\nexport const PinInputField = React.forwardRef(\n (\n {\n numberOfFields: length,\n onChange,\n type = \"number\",\n mask = false,\n otp = false,\n autoFocus = false,\n pasteFromClipboard = \"enabled\",\n spacing = \"small\",\n }: PinInputFieldProps,\n ref: React.Ref<PinInputFieldElementType>\n ) => {\n const [inputRefs, setInputRefs] = useState<React.RefObject<HTMLInputElement>[]>([]);\n const [values, setValues] = useState<string[]>([]);\n const [moveFocus, setMoveFocus] = useState<boolean>(true);\n const [focusedIndex, setFocusedIndex] = useState<number>(-1);\n\n const focus = useCallback(\n (index: number) => {\n const ref = inputRefs[index];\n ref?.current?.focus();\n },\n [inputRefs]\n );\n\n useEffect(() => {\n setInputRefs((inputRefs) => {\n const refs = Array(length)\n .fill(0)\n .map((_, i) => {\n const ref = inputRefs[i] || createRef();\n if (autoFocus && i === 0) {\n ref.current?.focus();\n }\n return ref;\n });\n return refs;\n });\n }, [length, autoFocus]);\n\n const focusNext = useCallback(\n (index: number) => {\n if (!moveFocus) return;\n const next = index + 1 < length ? index + 1 : null;\n if (next) {\n focus(next);\n }\n },\n [focus, length, moveFocus]\n );\n\n const setValue = useCallback(\n (value: string, index: number) => {\n const nextValues = [...values];\n nextValues[index] = value;\n setValues(nextValues);\n onChange?.(nextValues.join(\"\"));\n\n const isComplete =\n value !== \"\" &&\n nextValues.length === length &&\n nextValues.every((inputValue) => inputValue != null && inputValue !== \"\");\n\n if (!isComplete) {\n focusNext(index);\n }\n },\n [focusNext, length, onChange, values]\n );\n\n const getNextValue = useCallback((value: string, eventValue: string) => {\n let nextValue = eventValue;\n if (value?.length > 0) {\n if (value[0] === eventValue.charAt(0)) {\n nextValue = eventValue.charAt(1);\n } else if (value[0] === eventValue.charAt(1)) {\n nextValue = eventValue.charAt(0);\n }\n }\n return nextValue;\n }, []);\n\n const handleInputChange = (event: React.FormEvent<HTMLInputElement>, i: number) => {\n const eventValue = event.currentTarget.value;\n const currentValue = values[i];\n const nextValue = getNextValue(currentValue, eventValue);\n\n if (nextValue === \"\") {\n setValue(\"\", i);\n return;\n }\n\n if (eventValue.length > 1 && i < length - 1) {\n if (validate(eventValue, type)) {\n const nextValue = eventValue.split(\"\").filter((_, index) => index < length);\n setValues(nextValue);\n focus(i + nextValue.length < length ? i + nextValue.length : length - 1);\n onChange?.(nextValue.join(\"\"));\n }\n } else {\n if (validate(nextValue, type)) {\n setValue(nextValue, i);\n }\n setMoveFocus(true);\n }\n };\n\n const onKeyDown = (event: React.KeyboardEvent, i: number) => {\n if (event.key === \"Backspace\") {\n if ((event.target as HTMLInputElement).value === \"\") {\n if (i > 0) {\n const newIndex = i - 1;\n setValue(\"\", newIndex);\n focus(newIndex);\n setMoveFocus(true);\n }\n } else {\n setMoveFocus(false);\n }\n } else if (event.key === \"Escape\") {\n inputRefs[i].current?.blur();\n onBlur();\n } else if (event.key === \"ArrowRight\") {\n if (i < length - 1) {\n focus(i + 1);\n }\n } else if (event.key === \"ArrowLeft\") {\n if (i > 0) {\n focus(i - 1);\n }\n }\n };\n\n const onFocus = (e: React.FocusEvent<HTMLInputElement>, i: number) => {\n setFocusedIndex(i);\n setTimeout(() => {\n // https://github.com/facebook/react/issues/6483\n e.target.setSelectionRange(e.target.value.length, e.target.value.length);\n }, 0);\n };\n\n const onBlur = () => {\n setFocusedIndex(-1);\n };\n\n let classNames = [];\n\n if (spacing) {\n classNames.push(`spacing-${spacing}`);\n }\n\n return (\n <Element<PinInputFieldElementType> as={PinInputStyled} classNames={classNames} ref={ref}>\n {[...Array(length)].map((_, i) => (\n <InputField\n key={i}\n className=\"pin-input-field\"\n ref={inputRefs[i]}\n type={mask ? \"password\" : type === \"number\" ? \"tel\" : \"text\"}\n inputMode={type === \"number\" ? \"numeric\" : \"text\"}\n onChange={(e) => handleInputChange(e, i)}\n onKeyDown={(e) => onKeyDown(e, i)}\n onFocus={(e) => onFocus(e, i)}\n onBlur={onBlur}\n placeholder={focusedIndex !== i ? `\\u2981` : undefined}\n autoComplete={otp ? \"one-time-code\" : \"off\"}\n value={values[i] || \"\"}\n autoFocus={autoFocus && i === 0}\n onCopy={e=> pasteFromClipboard === \"disabled\" && e.preventDefault()}\n onPaste={e=> pasteFromClipboard === \"disabled\" && e.preventDefault()}\n />\n ))}\n </Element>\n );\n }\n);\n"],"names":["validate","value","type","test","PinInputField","React","forwardRef","numberOfFields","length","onChange","mask","otp","autoFocus","pasteFromClipboard","spacing","ref","inputRefs","setInputRefs","useState","values","setValues","moveFocus","setMoveFocus","focusedIndex","setFocusedIndex","focus","useCallback","index","current","useEffect","Array","fill","map","_","i","createRef","focusNext","next","setValue","nextValues","join","every","inputValue","getNextValue","eventValue","nextValue","charAt","onBlur","classNames","push","Element","as","PinInputStyled","InputField","key","className","inputMode","e","event","currentTarget","currentValue","split","filter","handleInputChange","onKeyDown","target","newIndex","blur","onFocus","setTimeout","setSelectionRange","placeholder","undefined","autoComplete","onCopy","preventDefault","onPaste"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"PinInputField.js","sources":["../../../../src/components/PinInputField/PinInputField.tsx"],"sourcesContent":["import React, { createRef, useCallback, useEffect, useState } from \"react\";\n\nimport { Element } from \"../Element/Element\";\nimport { CommonAndHTMLProps, SpacingTypes } from \"../Element/constants\";\nimport { InputField } from \"../Form/InputField/InputField\";\n\nimport { PinInputStyled } from \"./PinInputField.styled\";\n\n// prettier-ignore\ntype PinInputFieldCustomProps = {\n numberOfFields : number;\n onChange ? : (value : string) => void;\n type ? : \"alphanumeric\" | \"number\";\n mask ? : boolean;\n otp ? : boolean;\n autoFocus ? : boolean;\n pasteFromClipboard ? : \"enabled\" | \"disabled\";\n spacing ? : SpacingTypes;\n};\n\nexport type PinInputFieldElementType = HTMLDivElement;\nexport type PinInputFieldProps = Omit<CommonAndHTMLProps<PinInputFieldElementType>, keyof PinInputFieldCustomProps> &\n PinInputFieldCustomProps;\n\nfunction validate(value: string, type: \"alphanumeric\" | \"number\") {\n const NUMERIC_REGEX = /^[0-9]+$/;\n const ALPHA_NUMERIC_REGEX = /^[a-zA-Z0-9]+$/i;\n const regex = type === \"alphanumeric\" ? ALPHA_NUMERIC_REGEX : NUMERIC_REGEX;\n return regex.test(value);\n}\n\nexport const PinInputField = React.forwardRef(\n (\n {\n numberOfFields: length,\n onChange,\n type = \"number\",\n mask = false,\n otp = false,\n autoFocus = false,\n pasteFromClipboard = \"enabled\",\n spacing = \"small\",\n }: PinInputFieldProps,\n ref: React.Ref<PinInputFieldElementType>\n ) => {\n const [inputRefs, setInputRefs] = useState<React.RefObject<HTMLInputElement>[]>([]);\n const [values, setValues] = useState<string[]>([]);\n const [moveFocus, setMoveFocus] = useState<boolean>(true);\n const [focusedIndex, setFocusedIndex] = useState<number>(-1);\n\n const focus = useCallback(\n (index: number) => {\n const ref = inputRefs[index];\n ref?.current?.focus();\n },\n [inputRefs]\n );\n\n useEffect(() => {\n setInputRefs((inputRefs) => {\n const refs = Array(length)\n .fill(0)\n .map((_, i) => {\n const ref = inputRefs[i] || createRef();\n if (autoFocus && i === 0) {\n ref.current?.focus();\n }\n return ref;\n });\n return refs;\n });\n }, [length, autoFocus]);\n\n const focusNext = useCallback(\n (index: number) => {\n if (!moveFocus) return;\n const next = index + 1 < length ? index + 1 : null;\n if (next) {\n focus(next);\n }\n },\n [focus, length, moveFocus]\n );\n\n const setValue = useCallback(\n (value: string, index: number) => {\n const nextValues = [...values];\n nextValues[index] = value;\n setValues(nextValues);\n onChange?.(nextValues.join(\"\"));\n\n const isComplete =\n value !== \"\" &&\n nextValues.length === length &&\n nextValues.every((inputValue) => inputValue != null && inputValue !== \"\") &&\n index == length - 1;\n\n if (!isComplete) {\n setMoveFocus(true);\n focusNext(index);\n }\n },\n [focusNext, length, onChange, values]\n );\n\n const handleInputChange = (event: React.FormEvent<HTMLInputElement>, inputFieldIndex: number) => {\n const eventValue = event.currentTarget.value;\n const currentValue = values[inputFieldIndex];\n\n if (eventValue === \"\") {\n setValue(\"\", inputFieldIndex);\n return;\n }\n\n // Handle scenario where multiple characters are entered in a single InputField\n if (eventValue.length > 1 && inputFieldIndex < length - 1) {\n if (validate(eventValue, type)) {\n let nextValue: string[] = [];\n // In all cases, we need to ensure characters longer than the remaining fields are removed.\n if (currentValue == \"\") {\n // Case: Current input field is empty\n nextValue = eventValue.split(\"\").filter((_, j) => inputFieldIndex + j < length);\n } else if (event.currentTarget.selectionEnd === eventValue.length) {\n // Case: Current field has a value and cursor is after it\n nextValue = eventValue.split(\"\").filter((_, j) => j > 0 && inputFieldIndex + j - 1 < length);\n } else {\n // Case: Current field has a value and cursor is before it\n nextValue = eventValue\n .split(\"\")\n .filter((_, j) => j < eventValue.length - 1 && inputFieldIndex + j < length);\n }\n setValues((values) =>\n values.map((v, j) =>\n j >= inputFieldIndex && j < inputFieldIndex + nextValue.length\n ? nextValue[j - inputFieldIndex]\n : v\n )\n );\n focus(\n inputFieldIndex + nextValue.length < length ? inputFieldIndex + nextValue.length : length - 1\n );\n onChange?.(nextValue.join(\"\"));\n }\n } else {\n let nextValue = eventValue;\n if (currentValue?.length > 0) {\n if (currentValue[0] === eventValue.charAt(0)) {\n nextValue = eventValue.charAt(1);\n } else if (currentValue[0] === eventValue.charAt(1)) {\n nextValue = eventValue.charAt(0);\n }\n }\n if (validate(nextValue, type)) {\n setValue(nextValue, inputFieldIndex);\n }\n }\n };\n\n const onKeyDown = (event: React.KeyboardEvent, i: number) => {\n if (event.key === \"Backspace\") {\n if ((event.target as HTMLInputElement).value === \"\") {\n if (i > 0) {\n const newIndex = i - 1;\n setValue(\"\", newIndex);\n focus(newIndex);\n setMoveFocus(true);\n }\n } else {\n setMoveFocus(false);\n }\n } else if (event.key === \"Escape\") {\n inputRefs[i].current?.blur();\n onBlur();\n } else if (event.key === \"ArrowRight\") {\n if (i < length - 1) {\n focus(i + 1);\n }\n } else if (event.key === \"ArrowLeft\") {\n if (i > 0) {\n focus(i - 1);\n }\n }\n };\n\n const onFocus = (e: React.FocusEvent<HTMLInputElement>, i: number) => {\n setFocusedIndex(i);\n };\n\n // When moving around the InputElements using tab key, browsers automatically select\n // the value (if it exists) in the InputElement - which we want to disable. Additionally,\n // when an existing value is selected/highlighted and pasted over, there is no way to\n // clearly distinguish between the other 2 scenarios of pasting by keeping the cursor before\n // and after the existing value. Specific example: If the existing value is 5, the event\n // when highlighting and pasting '567' is the same as placing the cursor before the existing\n // value and pasting '67'. By disabling this, we eliminate one of these cases.\n // Is this a hack? Yes. Is there a better way? IDK. Does it matter? Not unless there is a\n // valid reason for users to need selecting a single InputElement within a PinInput.\n const onSelect = (e: React.SyntheticEvent<HTMLInputElement, Event>) => {\n const target = e.target as HTMLInputElement;\n setTimeout(() => {\n // https://github.com/facebook/react/issues/6483\n target.setSelectionRange(target.value.length, target.value.length);\n }, 0);\n };\n\n const onBlur = () => {\n setFocusedIndex(-1);\n };\n\n let classNames = [];\n\n if (spacing) {\n classNames.push(`spacing-${spacing}`);\n }\n\n return (\n <Element<PinInputFieldElementType> as={PinInputStyled} classNames={classNames} ref={ref}>\n {[...Array(length)].map((_, i) => (\n <InputField\n key={i}\n className=\"pin-input-field\"\n ref={inputRefs[i]}\n type={mask ? \"password\" : type === \"number\" ? \"tel\" : \"text\"}\n inputMode={type === \"number\" ? \"numeric\" : \"text\"}\n onChange={(e) => handleInputChange(e, i)}\n onKeyDown={(e) => onKeyDown(e, i)}\n onFocus={(e) => onFocus(e, i)}\n onSelect={(e) => onSelect(e)}\n onBlur={onBlur}\n placeholder={focusedIndex !== i ? `\\u2981` : undefined}\n autoComplete={otp ? \"one-time-code\" : \"off\"}\n value={values[i] || \"\"}\n autoFocus={autoFocus && i === 0}\n onCopy={(e) => pasteFromClipboard === \"disabled\" && e.preventDefault()}\n onPaste={(e) => pasteFromClipboard === \"disabled\" && e.preventDefault()}\n />\n ))}\n </Element>\n );\n }\n);\n"],"names":["validate","value","type","test","PinInputField","React","forwardRef","numberOfFields","length","onChange","mask","otp","autoFocus","pasteFromClipboard","spacing","ref","inputRefs","setInputRefs","useState","values","setValues","moveFocus","setMoveFocus","focusedIndex","setFocusedIndex","focus","useCallback","index","current","useEffect","Array","fill","map","_","i","createRef","focusNext","next","setValue","nextValues","join","every","inputValue","onBlur","classNames","push","Element","as","PinInputStyled","InputField","key","className","inputMode","e","event","inputFieldIndex","eventValue","currentTarget","currentValue","nextValue","split","filter","j","selectionEnd","v","charAt","handleInputChange","onKeyDown","target","newIndex","blur","onFocus","onSelect","setTimeout","setSelectionRange","placeholder","undefined","autoComplete","onCopy","preventDefault","onPaste"],"mappings":"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"}
|