fictoan-react 0.45.4 → 0.46.5

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 CHANGED
@@ -1,5 +1,12 @@
1
1
  # CHANGELOG
2
2
 
3
+ ### 0.46.5
4
+ - Add an imperative reset handler to reset `PinInputField` from the parent component with ref
5
+ - Add a `focusFirstInputOnReset` prop for the above change, to opt out of focusing on first input on reset
6
+
7
+ ### 0.45.5
8
+ - Add Tab component label prop to be used as reactnode for custom label
9
+
3
10
  ### 0.45.4
4
11
  - Fix PinInputField behaviour on paste or replace at position
5
12
 
@@ -9,8 +9,11 @@ declare type PinInputFieldCustomProps = {
9
9
  autoFocus?: boolean;
10
10
  pasteFromClipboard?: "enabled" | "disabled";
11
11
  spacing?: SpacingTypes;
12
+ focusFirstInputOnReset?: boolean;
13
+ };
14
+ export declare type PinInputFieldElementType = HTMLDivElement & {
15
+ reset: () => void;
12
16
  };
13
- export declare type PinInputFieldElementType = HTMLDivElement;
14
17
  export declare type PinInputFieldProps = Omit<CommonAndHTMLProps<PinInputFieldElementType>, keyof PinInputFieldCustomProps> & PinInputFieldCustomProps;
15
- export declare const PinInputField: React.ForwardRefExoticComponent<Omit<CommonAndHTMLProps<HTMLDivElement>, keyof PinInputFieldCustomProps> & PinInputFieldCustomProps & React.RefAttributes<HTMLDivElement>>;
18
+ export declare const PinInputField: React.ForwardRefExoticComponent<Omit<CommonAndHTMLProps<PinInputFieldElementType>, keyof PinInputFieldCustomProps> & PinInputFieldCustomProps & React.RefAttributes<PinInputFieldElementType>>;
16
19
  export {};
@@ -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,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;
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",focusFirstInputOnReset:f=!0},F)=>{const g=e.useRef(null),[y,h]=e.useState([]),[v,b]=e.useState([]),[j,I]=e.useState(!0),[q,A]=e.useState(-1),C=e.useCallback((e=>{var t;const r=y[e];null===(t=null==r?void 0:r.current)||void 0===t||t.focus()}),[y]);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]),e.useImperativeHandle(F,(()=>Object.assign(Object.assign({},g.current),{reset(){x()}})),[y]);const k=e.useCallback((e=>{if(!j)return;const t=e+1<n?e+1:null;t&&C(t)}),[C,n,j]),x=e.useCallback((()=>{b(Array(n).fill("")),null==s||s(""),f&&(C(0),A(0))}),[n,s,C]),S=e.useCallback(((e,t)=>{const r=[...v];r[t]=e,b(r),null==s||s(r.join(""));""!==e&&r.length===n&&r.every((e=>null!=e&&""!==e))&&t==n-1||(I(!0),k(t))}),[k,n,s,v]),E=()=>{A(-1)};let R=[];return m&&R.push(`spacing-${m}`),u.default.createElement(t.Element,{as:l.PinInputStyled,classNames:R,ref:g},[...Array(n)].map(((e,t)=>u.default.createElement(r.InputField,{key:t,className:"pin-input-field",ref:y[t],type:i?"password":"number"===o?"tel":"text",inputMode:"number"===o?"numeric":"text",onChange:e=>((e,t)=>{const r=e.currentTarget.value,l=v[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)),b((e=>e.map(((e,r)=>r>=t&&r<t+u.length?u[r-t]:e)))),C(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)&&S(e,t)}else S("",t)})(e,t),onKeyDown:e=>((e,t)=>{var r;if("Backspace"===e.key)if(""===e.target.value){if(t>0){const e=t-1;S("",e),C(e),I(!0)}}else I(!1);else"Escape"===e.key?(null===(r=y[t].current)||void 0===r||r.blur(),E()):"ArrowRight"===e.key?t<n-1&&C(t+1):"ArrowLeft"===e.key&&t>0&&C(t-1)})(e,t),onFocus:e=>((e,t)=>{A(t)})(0,t),onSelect:e=>(e=>{const t=e.target;setTimeout((()=>{t.setSelectionRange(t.value.length,t.value.length)}),0)})(e),onBlur:E,placeholder:q!==t?"⦁":void 0,autoComplete:c?"one-time-code":"off",value:v[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 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":"+uBAwBA,SAASA,EAASC,EAAeC,UAGN,iBAATA,EADc,kBADN,YAGTC,KAAKF,SAGTG,EAAgBC,UAAMC,YAC/B,EAEQC,eAAgBC,EAChBC,SAAAA,EACAP,KAAAA,EAAO,SACPQ,KAAAA,GAAO,EACPC,IAAAA,GAAM,EACNC,UAAAA,GAAY,EACZC,mBAAAA,EAAqB,UACrBC,QAAAA,EAAU,SAEdC,WAEOC,EAAWC,GAAgBC,WAA8C,KACzEC,EAAQC,GAAaF,WAAmB,KACxCG,EAAWC,GAAgBJ,YAAkB,IAC7CK,EAAcC,GAAmBN,YAAkB,GAEpDO,EAAQC,eACTC,gBACSZ,EAAMC,EAAUW,aACtBZ,MAAAA,SAAAA,EAAKa,wBAASH,UAElB,CAACT,IAGLa,aAAU,KACNZ,GAAcD,GACGc,MAAMtB,GACduB,KAAK,GACLC,KAAI,CAACC,EAAGC,iBACCnB,EAAMC,EAAUkB,IAAMC,qBACxBvB,GAAmB,IAANsB,cACbnB,EAAIa,wBAASH,SAEVV,SAIpB,CAACP,EAAQI,UAENwB,EAAYV,eACbC,QACQN,EAAW,aACVgB,EAAOV,EAAQ,EAAInB,EAASmB,EAAQ,EAAI,KAC1CU,GACAZ,EAAMY,KAGd,CAACZ,EAAOjB,EAAQa,IAGdiB,EAAWZ,eACb,CAACzB,EAAe0B,WACNY,EAAa,IAAIpB,GACvBoB,EAAWZ,GAAS1B,EACpBmB,EAAUmB,GACV9B,MAAAA,GAAAA,EAAW8B,EAAWC,KAAK,KAGb,KAAVvC,GACAsC,EAAW/B,SAAWA,GACtB+B,EAAWE,OAAOC,GAA6B,MAAdA,GAAqC,KAAfA,KACvDf,GAASnB,EAAS,IAGlBc,GAAa,GACbc,EAAUT,MAGlB,CAACS,EAAW5B,EAAQC,EAAUU,IAuG5BwB,EAAS,KACXnB,GAAiB,QAGjBoB,EAAa,UAEb9B,GACA8B,EAAWC,gBAAgB/B,KAI3BT,wBAACyC,WAAkCC,GAAIC,iBAAgBJ,WAAYA,EAAY7B,IAAKA,GAC/E,IAAIe,MAAMtB,IAASwB,KAAI,CAACC,EAAGC,IACxB7B,wBAAC4C,cACGC,IAAKhB,EACLiB,UAAU,kBACVpC,IAAKC,EAAUkB,GACfhC,KAAMQ,EAAO,WAAsB,WAATR,EAAoB,MAAQ,OACtDkD,UAAoB,WAATlD,EAAoB,UAAY,OAC3CO,SAAW4C,GAvHD,EAACC,EAA0CC,WAC3DC,EAAaF,EAAMG,cAAcxD,MACjCyD,EAAevC,EAAOoC,MAET,KAAfC,KAMAA,EAAWhD,OAAS,GAAK+C,EAAkB/C,EAAS,MAChDR,EAASwD,EAAYtD,GAAO,KACxByD,EAAsB,GAItBA,EAFgB,IAAhBD,EAEYF,EAAWI,MAAM,IAAIC,QAAO,CAAC5B,EAAG6B,IAAMP,EAAkBO,EAAItD,IACjE8C,EAAMG,cAAcM,eAAiBP,EAAWhD,OAE3CgD,EAAWI,MAAM,IAAIC,QAAO,CAAC5B,EAAG6B,IAAMA,EAAI,GAAKP,EAAkBO,EAAI,EAAItD,IAGzEgD,EACPI,MAAM,IACNC,QAAO,CAAC5B,EAAG6B,IAAMA,EAAIN,EAAWhD,OAAS,GAAK+C,EAAkBO,EAAItD,IAE7EY,GAAWD,GACPA,EAAOa,KAAI,CAACgC,EAAGF,IACXA,GAAKP,GAAmBO,EAAIP,EAAkBI,EAAUnD,OAClDmD,EAAUG,EAAIP,GACdS,MAGdvC,EACI8B,EAAkBI,EAAUnD,OAASA,EAAS+C,EAAkBI,EAAUnD,OAASA,EAAS,GAEhGC,MAAAA,GAAAA,EAAWkD,EAAUnB,KAAK,UAE3B,KACCmB,EAAYH,GACZE,MAAAA,SAAAA,EAAclD,QAAS,IACnBkD,EAAa,KAAOF,EAAWS,OAAO,GACtCN,EAAYH,EAAWS,OAAO,GACvBP,EAAa,KAAOF,EAAWS,OAAO,KAC7CN,EAAYH,EAAWS,OAAO,KAGlCjE,EAAS2D,EAAWzD,IACpBoC,EAASqB,EAAWJ,QA3CxBjB,EAAS,GAAIiB,IAkHYW,CAAkBb,EAAGnB,GACtCiC,UAAYd,GAnEV,EAACC,EAA4BpB,cACzB,cAAdoB,EAAMJ,OAC2C,KAA5CI,EAAMc,OAA4BnE,UAC/BiC,EAAI,EAAG,OACDmC,EAAWnC,EAAI,EACrBI,EAAS,GAAI+B,GACb5C,EAAM4C,GACN/C,GAAa,SAGjBA,GAAa,OAEI,WAAdgC,EAAMJ,eACblC,EAAUkB,GAAGN,wBAAS0C,OACtB3B,KACqB,eAAdW,EAAMJ,IACThB,EAAI1B,EAAS,GACbiB,EAAMS,EAAI,GAEO,cAAdoB,EAAMJ,KACThB,EAAI,GACJT,EAAMS,EAAI,IA8CYiC,CAAUd,EAAGnB,GAC/BqC,QAAUlB,GA1CV,EAACA,EAAuCnB,KACpDV,EAAgBU,IAyCYqC,CAAQlB,EAAGnB,GAC3BsC,SAAWnB,GA9BTA,CAAAA,UACRe,EAASf,EAAEe,OACjBK,YAAW,KAEPL,EAAOM,kBAAkBN,EAAOnE,MAAMO,OAAQ4D,EAAOnE,MAAMO,UAC5D,IAyB0BgE,CAASnB,GAC1BV,OAAQA,EACRgC,YAAapD,IAAiBW,WAAe0C,EAC7CC,aAAclE,EAAM,gBAAkB,MACtCV,MAAOkB,EAAOe,IAAM,GACpBtB,UAAWA,GAAmB,IAANsB,EACxB4C,OAASzB,GAA6B,aAAvBxC,GAAqCwC,EAAE0B,iBACtDC,QAAU3B,GAA6B,aAAvBxC,GAAqCwC,EAAE0B"}
1
+ {"version":3,"file":"PinInputField.js","sources":["../../../../src/components/PinInputField/PinInputField.tsx"],"sourcesContent":["import React, { createRef, useCallback, useEffect, useImperativeHandle, useRef, 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 focusFirstInputOnReset ? : boolean;\n};\n\nexport type PinInputFieldElementType = HTMLDivElement & { reset: () => void };\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 focusFirstInputOnReset = true,\n }: PinInputFieldProps,\n ref: React.Ref<PinInputFieldElementType>\n ) => {\n const pinInputFieldRef = useRef<PinInputFieldElementType>(null);\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 useImperativeHandle(\n ref,\n () => {\n return {\n ...pinInputFieldRef.current,\n reset() {\n handleResetPinInput();\n },\n };\n },\n [inputRefs]\n );\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 handleResetPinInput = useCallback(() => {\n setValues(Array(length).fill(\"\"));\n onChange?.(\"\");\n if (focusFirstInputOnReset) {\n focus(0);\n setFocusedIndex(0);\n }\n }, [length, onChange, focus]);\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={pinInputFieldRef}>\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","focusFirstInputOnReset","ref","pinInputFieldRef","useRef","inputRefs","setInputRefs","useState","values","setValues","moveFocus","setMoveFocus","focusedIndex","setFocusedIndex","focus","useCallback","index","current","useEffect","Array","fill","map","_","i","createRef","useImperativeHandle","reset","handleResetPinInput","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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"}
@@ -2,14 +2,14 @@ import React from "react";
2
2
  import { CommonAndHTMLProps } from "../Element/constants";
3
3
  interface TabType {
4
4
  key: string;
5
- label: string;
5
+ label: React.ReactNode;
6
6
  content: React.ReactNode;
7
7
  }
8
8
  export interface TabsCustomProps {
9
9
  tabs: TabType[];
10
10
  /** wrapper to render additional content inside the nav along with tab labels */
11
11
  additionalNavContentWrapper?: React.ReactNode;
12
- defaultActiveTab?: string;
12
+ defaultActiveTab?: React.ReactNode;
13
13
  }
14
14
  export declare type TabsElementType = HTMLDivElement;
15
15
  export declare type TabsProps = Omit<CommonAndHTMLProps<TabsElementType>, keyof TabsCustomProps> & TabsCustomProps;
@@ -1 +1 @@
1
- {"version":3,"file":"Tabs.js","sources":["../../../../src/components/Tabs/Tabs.tsx"],"sourcesContent":["import React, { useEffect } from \"react\";\n\nimport { Element } from \"../Element/Element\";\nimport { CommonAndHTMLProps } from \"../Element/constants\";\nimport { HRule } from \"../HRule/HRule\";\nimport { Text } from \"../Typography/Text\";\n\nimport { TabsStyled } from \"./Tabs.styled\";\n\n// prettier-ignore\ninterface TabType {\n key : string;\n label : string;\n content : React.ReactNode;\n}\n\n// prettier-ignore\nexport interface TabsCustomProps {\n tabs : TabType[];\n /** wrapper to render additional content inside the nav along with tab labels */\n additionalNavContentWrapper?: React.ReactNode; \n defaultActiveTab?: string;\n}\n\nexport type TabsElementType = HTMLDivElement;\nexport type TabsProps = Omit<CommonAndHTMLProps<TabsElementType>, keyof TabsCustomProps> & TabsCustomProps;\n\nexport const Tabs = React.forwardRef(\n ({ tabs, additionalNavContentWrapper, defaultActiveTab, ...props }: TabsProps, ref: React.Ref<TabsElementType>) => {\n const index = tabs.findIndex(tab => tab.key === defaultActiveTab);\n const defaultTabIndex = index > -1 ? index : 0;\n const [activeTab, setActiveTab] = React.useState<TabType | undefined>(tabs.length > 0 ? tabs[defaultTabIndex] : undefined);\n const [isExiting, setIsExiting] = React.useState<boolean>(false);\n\n const handleTabChange = (tab: TabType) => {\n setIsExiting(true);\n setTimeout(() => {\n setIsExiting(false);\n setActiveTab(tab);\n }, 120);\n };\n\n useEffect(() => {\n if (tabs.length > 0) {\n const matchingTab = tabs.find((tab) => tab.key === activeTab?.key);\n if (matchingTab) {\n handleTabChange(matchingTab);\n } else {\n handleTabChange(tabs[0]);\n }\n } else {\n setActiveTab(undefined);\n }\n }, [tabs]);\n\n return (\n <Element<TabsElementType> as={TabsStyled} ref={ref} {...props}>\n {tabs.length > 0 && activeTab ? (\n <>\n <nav>\n <ul className=\"tab-labels-list\">\n {tabs.map((tab) => (\n <li key={tab.key}>\n <Text\n className={`is-clickable ${tab.key === activeTab.key ? \"is-active\" : \"\"} }`}\n onClick={() => handleTabChange(tab)}\n marginBottom=\"none\"\n >\n {tab.label}\n </Text>\n </li>\n ))}\n </ul>\n {additionalNavContentWrapper && <>{additionalNavContentWrapper}</>}\n </nav>\n <HRule kind=\"secondary\" marginTop=\"none\" marginBottom=\"micro\" />\n <Element as=\"div\" className={`tabs-content ${isExiting ? \"exiting\" : \"\"}`}>\n {activeTab.content}\n </Element>\n </>\n ) : (\n <></>\n )}\n </Element>\n );\n }\n);\n"],"names":["Tabs","React","forwardRef","_a","ref","tabs","additionalNavContentWrapper","defaultActiveTab","props","index","findIndex","tab","key","defaultTabIndex","activeTab","setActiveTab","useState","length","undefined","isExiting","setIsExiting","handleTabChange","setTimeout","useEffect","matchingTab","find","Element","as","TabsStyled","className","map","Text","onClick","marginBottom","label","HRule","kind","marginTop","content"],"mappings":"yYA2BaA,EAAOC,UAAMC,YACtB,CAACC,EAA8EC,SAA9EC,KAAEA,EAAFC,4BAAQA,EAARC,iBAAqCA,KAAqBC,aAA1D,iEACSC,EAAQJ,EAAKK,WAAUC,GAAOA,EAAIC,MAAQL,IAC1CM,EAAkBJ,GAAS,EAAIA,EAAQ,GACtCK,EAAWC,GAAgBd,UAAMe,SAA8BX,EAAKY,OAAS,EAAIZ,EAAKQ,QAAmBK,IACzGC,EAAWC,GAAgBnB,UAAMe,UAAkB,GAEpDK,EAAmBV,IACrBS,GAAa,GACbE,YAAW,KACPF,GAAa,GACbL,EAAaJ,KACd,aAGPY,aAAU,QACFlB,EAAKY,OAAS,EAAG,OACXO,EAAcnB,EAAKoB,MAAMd,GAAQA,EAAIC,OAAQE,MAAAA,SAAAA,EAAWF,OAE1DS,EADAG,GAGgBnB,EAAK,SAGzBU,OAAaG,KAElB,CAACb,IAGAJ,wBAACyB,yBAAyBC,GAAIC,aAAYxB,IAAKA,GAASI,GACnDH,EAAKY,OAAS,GAAKH,EAChBb,gDACIA,mCACIA,8BAAI4B,UAAU,mBACTxB,EAAKyB,KAAKnB,GACPV,8BAAIW,IAAKD,EAAIC,KACTX,wBAAC8B,QACGF,0BAA2BlB,EAAIC,MAAQE,EAAUF,IAAM,YAAc,OACrEoB,QAAS,IAAMX,EAAgBV,GAC/BsB,aAAa,QAEZtB,EAAIuB,WAKpB5B,GAA+BL,gDAAGK,IAEvCL,wBAACkC,SAAMC,KAAK,YAAYC,UAAU,OAAOJ,aAAa,UACtDhC,wBAACyB,WAAQC,GAAG,MAAME,2BAA2BV,EAAY,UAAY,KAChEL,EAAUwB,UAInBrC"}
1
+ {"version":3,"file":"Tabs.js","sources":["../../../../src/components/Tabs/Tabs.tsx"],"sourcesContent":["import React, { useEffect } from \"react\";\n\nimport { Element } from \"../Element/Element\";\nimport { CommonAndHTMLProps } from \"../Element/constants\";\nimport { HRule } from \"../HRule/HRule\";\nimport { Text } from \"../Typography/Text\";\n\nimport { TabsStyled } from \"./Tabs.styled\";\n\n// prettier-ignore\ninterface TabType {\n key : string;\n label : React.ReactNode;\n content : React.ReactNode;\n}\n\n// prettier-ignore\nexport interface TabsCustomProps {\n tabs : TabType[];\n /** wrapper to render additional content inside the nav along with tab labels */\n additionalNavContentWrapper?: React.ReactNode; \n defaultActiveTab?: React.ReactNode;\n}\n\nexport type TabsElementType = HTMLDivElement;\nexport type TabsProps = Omit<CommonAndHTMLProps<TabsElementType>, keyof TabsCustomProps> & TabsCustomProps;\n\nexport const Tabs = React.forwardRef(\n ({ tabs, additionalNavContentWrapper, defaultActiveTab, ...props }: TabsProps, ref: React.Ref<TabsElementType>) => {\n const index = tabs.findIndex((tab) => tab.key === defaultActiveTab);\n const defaultTabIndex = index > -1 ? index : 0;\n const [activeTab, setActiveTab] = React.useState<TabType | undefined>(\n tabs.length > 0 ? tabs[defaultTabIndex] : undefined\n );\n const [isExiting, setIsExiting] = React.useState<boolean>(false);\n\n const handleTabChange = (tab: TabType) => {\n setIsExiting(true);\n setTimeout(() => {\n setIsExiting(false);\n setActiveTab(tab);\n }, 120);\n };\n\n useEffect(() => {\n if (tabs.length > 0) {\n const matchingTab = tabs.find((tab) => tab.key === activeTab?.key);\n if (matchingTab) {\n handleTabChange(matchingTab);\n } else {\n handleTabChange(tabs[0]);\n }\n } else {\n setActiveTab(undefined);\n }\n }, [tabs]);\n\n return (\n <Element<TabsElementType> as={TabsStyled} ref={ref} {...props}>\n {tabs.length > 0 && activeTab ? (\n <>\n <nav>\n <ul className=\"tab-labels-list\">\n {tabs.map((tab) => (\n <li key={tab.key}>\n <Text\n className={`is-clickable ${tab.key === activeTab.key ? \"is-active\" : \"\"} }`}\n onClick={() => handleTabChange(tab)}\n marginBottom=\"none\"\n >\n {tab.label}\n </Text>\n </li>\n ))}\n </ul>\n {additionalNavContentWrapper && <>{additionalNavContentWrapper}</>}\n </nav>\n <HRule kind=\"secondary\" marginTop=\"none\" marginBottom=\"micro\" />\n <Element as=\"div\" className={`tabs-content ${isExiting ? \"exiting\" : \"\"}`}>\n {activeTab.content}\n </Element>\n </>\n ) : (\n <></>\n )}\n </Element>\n );\n }\n);\n"],"names":["Tabs","React","forwardRef","_a","ref","tabs","additionalNavContentWrapper","defaultActiveTab","props","index","findIndex","tab","key","defaultTabIndex","activeTab","setActiveTab","useState","length","undefined","isExiting","setIsExiting","handleTabChange","setTimeout","useEffect","matchingTab","find","Element","as","TabsStyled","className","map","Text","onClick","marginBottom","label","HRule","kind","marginTop","content"],"mappings":"yYA2BaA,EAAOC,UAAMC,YACtB,CAACC,EAA8EC,SAA9EC,KAAEA,EAAFC,4BAAQA,EAARC,iBAAqCA,KAAqBC,aAA1D,iEACSC,EAAQJ,EAAKK,WAAWC,GAAQA,EAAIC,MAAQL,IAC5CM,EAAkBJ,GAAS,EAAIA,EAAQ,GACtCK,EAAWC,GAAgBd,UAAMe,SACpCX,EAAKY,OAAS,EAAIZ,EAAKQ,QAAmBK,IAEvCC,EAAWC,GAAgBnB,UAAMe,UAAkB,GAEpDK,EAAmBV,IACrBS,GAAa,GACbE,YAAW,KACPF,GAAa,GACbL,EAAaJ,KACd,aAGPY,aAAU,QACFlB,EAAKY,OAAS,EAAG,OACXO,EAAcnB,EAAKoB,MAAMd,GAAQA,EAAIC,OAAQE,MAAAA,SAAAA,EAAWF,OAE1DS,EADAG,GAGgBnB,EAAK,SAGzBU,OAAaG,KAElB,CAACb,IAGAJ,wBAACyB,yBAAyBC,GAAIC,aAAYxB,IAAKA,GAASI,GACnDH,EAAKY,OAAS,GAAKH,EAChBb,gDACIA,mCACIA,8BAAI4B,UAAU,mBACTxB,EAAKyB,KAAKnB,GACPV,8BAAIW,IAAKD,EAAIC,KACTX,wBAAC8B,QACGF,0BAA2BlB,EAAIC,MAAQE,EAAUF,IAAM,YAAc,OACrEoB,QAAS,IAAMX,EAAgBV,GAC/BsB,aAAa,QAEZtB,EAAIuB,WAKpB5B,GAA+BL,gDAAGK,IAEvCL,wBAACkC,SAAMC,KAAK,YAAYC,UAAU,OAAOJ,aAAa,UACtDhC,wBAACyB,WAAQC,GAAG,MAAME,2BAA2BV,EAAY,UAAY,KAChEL,EAAUwB,UAInBrC"}
@@ -9,8 +9,11 @@ declare type PinInputFieldCustomProps = {
9
9
  autoFocus?: boolean;
10
10
  pasteFromClipboard?: "enabled" | "disabled";
11
11
  spacing?: SpacingTypes;
12
+ focusFirstInputOnReset?: boolean;
13
+ };
14
+ export declare type PinInputFieldElementType = HTMLDivElement & {
15
+ reset: () => void;
12
16
  };
13
- export declare type PinInputFieldElementType = HTMLDivElement;
14
17
  export declare type PinInputFieldProps = Omit<CommonAndHTMLProps<PinInputFieldElementType>, keyof PinInputFieldCustomProps> & PinInputFieldCustomProps;
15
- export declare const PinInputField: React.ForwardRefExoticComponent<Omit<CommonAndHTMLProps<HTMLDivElement>, keyof PinInputFieldCustomProps> & PinInputFieldCustomProps & React.RefAttributes<HTMLDivElement>>;
18
+ export declare const PinInputField: React.ForwardRefExoticComponent<Omit<CommonAndHTMLProps<PinInputFieldElementType>, keyof PinInputFieldCustomProps> & PinInputFieldCustomProps & React.RefAttributes<PinInputFieldElementType>>;
16
19
  export {};
@@ -1,2 +1,2 @@
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};
1
+ import e,{useRef as t,useState as r,useCallback as n,useEffect as l,createRef as o,useImperativeHandle as s}from"react";import{E as a}from"../../external/Element.js";import{InputField as i}from"../Form/InputField/InputField.js";import{PinInputStyled as u}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 m(e,t){return("alphanumeric"===t?/^[a-zA-Z0-9]+$/i:/^[0-9]+$/).test(e)}const p=/*#__PURE__*/e.forwardRef((({numberOfFields:p,onChange:c,type:f="number",mask:d=!1,otp:F=!1,autoFocus:g=!1,pasteFromClipboard:h="enabled",spacing:y="small",focusFirstInputOnReset:v=!0},j)=>{const I=t(null),[b,A]=r([]),[x,C]=r([]),[k,E]=r(!0),[w,T]=r(-1),B=n((e=>{var t;const r=b[e];null===(t=null==r?void 0:r.current)||void 0===t||t.focus()}),[b]);l((()=>{A((e=>Array(p).fill(0).map(((t,r)=>{var n;const l=e[r]||/*#__PURE__*/o();return g&&0===r&&(null===(n=l.current)||void 0===n||n.focus()),l}))))}),[p,g]),s(j,(()=>Object.assign(Object.assign({},I.current),{reset(){O()}})),[b]);const D=n((e=>{if(!k)return;const t=e+1<p?e+1:null;t&&B(t)}),[B,p,k]),O=n((()=>{C(Array(p).fill("")),null==c||c(""),v&&(B(0),T(0))}),[p,c,B]),R=n(((e,t)=>{const r=[...x];r[t]=e,C(r),null==c||c(r.join(""));""!==e&&r.length===p&&r.every((e=>null!=e&&""!==e))&&t==p-1||(E(!0),D(t))}),[D,p,c,x]),S=()=>{T(-1)};let L=[];return y&&L.push(`spacing-${y}`),/*#__PURE__*/e.createElement(a,{as:u,classNames:L,ref:I},[...Array(p)].map(((t,r)=>/*#__PURE__*/e.createElement(i,{key:r,className:"pin-input-field",ref:b[r],type:d?"password":"number"===f?"tel":"text",inputMode:"number"===f?"numeric":"text",onChange:e=>((e,t)=>{const r=e.currentTarget.value,n=x[t];if(""!==r)if(r.length>1&&t<p-1){if(m(r,f)){let l=[];l=""==n?r.split("").filter(((e,r)=>t+r<p)):e.currentTarget.selectionEnd===r.length?r.split("").filter(((e,r)=>r>0&&t+r-1<p)):r.split("").filter(((e,n)=>n<r.length-1&&t+n<p)),C((e=>e.map(((e,r)=>r>=t&&r<t+l.length?l[r-t]:e)))),B(t+l.length<p?t+l.length:p-1),null==c||c(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))),m(e,f)&&R(e,t)}else R("",t)})(e,r),onKeyDown:e=>((e,t)=>{var r;if("Backspace"===e.key)if(""===e.target.value){if(t>0){const e=t-1;R("",e),B(e),E(!0)}}else E(!1);else"Escape"===e.key?(null===(r=b[t].current)||void 0===r||r.blur(),S()):"ArrowRight"===e.key?t<p-1&&B(t+1):"ArrowLeft"===e.key&&t>0&&B(t-1)})(e,r),onFocus:e=>((e,t)=>{T(t)})(0,r),onSelect:e=>(e=>{const t=e.target;setTimeout((()=>{t.setSelectionRange(t.value.length,t.value.length)}),0)})(e),onBlur:S,placeholder:w!==r?"⦁":void 0,autoComplete:F?"one-time-code":"off",value:x[r]||"",autoFocus:g&&0===r,onCopy:e=>"disabled"===h&&e.preventDefault(),onPaste:e=>"disabled"===h&&e.preventDefault()}))))}));export{p 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 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
+ {"version":3,"file":"PinInputField.js","sources":["../../../../src/components/PinInputField/PinInputField.tsx"],"sourcesContent":["import React, { createRef, useCallback, useEffect, useImperativeHandle, useRef, 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 focusFirstInputOnReset ? : boolean;\n};\n\nexport type PinInputFieldElementType = HTMLDivElement & { reset: () => void };\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 focusFirstInputOnReset = true,\n }: PinInputFieldProps,\n ref: React.Ref<PinInputFieldElementType>\n ) => {\n const pinInputFieldRef = useRef<PinInputFieldElementType>(null);\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 useImperativeHandle(\n ref,\n () => {\n return {\n ...pinInputFieldRef.current,\n reset() {\n handleResetPinInput();\n },\n };\n },\n [inputRefs]\n );\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 handleResetPinInput = useCallback(() => {\n setValues(Array(length).fill(\"\"));\n onChange?.(\"\");\n if (focusFirstInputOnReset) {\n focus(0);\n setFocusedIndex(0);\n }\n }, [length, onChange, focus]);\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={pinInputFieldRef}>\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","focusFirstInputOnReset","ref","pinInputFieldRef","useRef","inputRefs","setInputRefs","useState","values","setValues","moveFocus","setMoveFocus","focusedIndex","setFocusedIndex","focus","useCallback","index","current","useEffect","Array","fill","map","_","i","createRef","useImperativeHandle","reset","handleResetPinInput","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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"}
@@ -2,14 +2,14 @@ import React from "react";
2
2
  import { CommonAndHTMLProps } from "../Element/constants";
3
3
  interface TabType {
4
4
  key: string;
5
- label: string;
5
+ label: React.ReactNode;
6
6
  content: React.ReactNode;
7
7
  }
8
8
  export interface TabsCustomProps {
9
9
  tabs: TabType[];
10
10
  /** wrapper to render additional content inside the nav along with tab labels */
11
11
  additionalNavContentWrapper?: React.ReactNode;
12
- defaultActiveTab?: string;
12
+ defaultActiveTab?: React.ReactNode;
13
13
  }
14
14
  export declare type TabsElementType = HTMLDivElement;
15
15
  export declare type TabsProps = Omit<CommonAndHTMLProps<TabsElementType>, keyof TabsCustomProps> & TabsCustomProps;
@@ -1 +1 @@
1
- {"version":3,"file":"Tabs.js","sources":["../../../../src/components/Tabs/Tabs.tsx"],"sourcesContent":["import React, { useEffect } from \"react\";\n\nimport { Element } from \"../Element/Element\";\nimport { CommonAndHTMLProps } from \"../Element/constants\";\nimport { HRule } from \"../HRule/HRule\";\nimport { Text } from \"../Typography/Text\";\n\nimport { TabsStyled } from \"./Tabs.styled\";\n\n// prettier-ignore\ninterface TabType {\n key : string;\n label : string;\n content : React.ReactNode;\n}\n\n// prettier-ignore\nexport interface TabsCustomProps {\n tabs : TabType[];\n /** wrapper to render additional content inside the nav along with tab labels */\n additionalNavContentWrapper?: React.ReactNode; \n defaultActiveTab?: string;\n}\n\nexport type TabsElementType = HTMLDivElement;\nexport type TabsProps = Omit<CommonAndHTMLProps<TabsElementType>, keyof TabsCustomProps> & TabsCustomProps;\n\nexport const Tabs = React.forwardRef(\n ({ tabs, additionalNavContentWrapper, defaultActiveTab, ...props }: TabsProps, ref: React.Ref<TabsElementType>) => {\n const index = tabs.findIndex(tab => tab.key === defaultActiveTab);\n const defaultTabIndex = index > -1 ? index : 0;\n const [activeTab, setActiveTab] = React.useState<TabType | undefined>(tabs.length > 0 ? tabs[defaultTabIndex] : undefined);\n const [isExiting, setIsExiting] = React.useState<boolean>(false);\n\n const handleTabChange = (tab: TabType) => {\n setIsExiting(true);\n setTimeout(() => {\n setIsExiting(false);\n setActiveTab(tab);\n }, 120);\n };\n\n useEffect(() => {\n if (tabs.length > 0) {\n const matchingTab = tabs.find((tab) => tab.key === activeTab?.key);\n if (matchingTab) {\n handleTabChange(matchingTab);\n } else {\n handleTabChange(tabs[0]);\n }\n } else {\n setActiveTab(undefined);\n }\n }, [tabs]);\n\n return (\n <Element<TabsElementType> as={TabsStyled} ref={ref} {...props}>\n {tabs.length > 0 && activeTab ? (\n <>\n <nav>\n <ul className=\"tab-labels-list\">\n {tabs.map((tab) => (\n <li key={tab.key}>\n <Text\n className={`is-clickable ${tab.key === activeTab.key ? \"is-active\" : \"\"} }`}\n onClick={() => handleTabChange(tab)}\n marginBottom=\"none\"\n >\n {tab.label}\n </Text>\n </li>\n ))}\n </ul>\n {additionalNavContentWrapper && <>{additionalNavContentWrapper}</>}\n </nav>\n <HRule kind=\"secondary\" marginTop=\"none\" marginBottom=\"micro\" />\n <Element as=\"div\" className={`tabs-content ${isExiting ? \"exiting\" : \"\"}`}>\n {activeTab.content}\n </Element>\n </>\n ) : (\n <></>\n )}\n </Element>\n );\n }\n);\n"],"names":["Tabs","React","forwardRef","_a","ref","tabs","additionalNavContentWrapper","defaultActiveTab","props","index","findIndex","tab","key","defaultTabIndex","activeTab","setActiveTab","useState","length","undefined","isExiting","setIsExiting","handleTabChange","setTimeout","useEffect","matchingTab","find","Element","as","TabsStyled","className","map","Text","onClick","marginBottom","label","HRule","kind","marginTop","content"],"mappings":"gSA2BaA,eAAOC,EAAMC,YACtB,CAACC,EAA8EC,SAA9EC,KAAEA,EAAFC,4BAAQA,EAARC,iBAAqCA,KAAqBC,MAA1D,iEACSC,EAAQJ,EAAKK,WAAUC,GAAOA,EAAIC,MAAQL,IAC1CM,EAAkBJ,GAAS,EAAIA,EAAQ,GACtCK,EAAWC,GAAgBd,EAAMe,SAA8BX,EAAKY,OAAS,EAAIZ,EAAKQ,QAAmBK,IACzGC,EAAWC,GAAgBnB,EAAMe,UAAkB,GAEpDK,EAAmBV,IACrBS,GAAa,GACbE,YAAW,KACPF,GAAa,GACbL,EAAaJ,KACd,aAGPY,GAAU,QACFlB,EAAKY,OAAS,EAAG,OACXO,EAAcnB,EAAKoB,MAAMd,GAAQA,EAAIC,OAAQE,MAAAA,SAAAA,EAAWF,OAE1DS,EADAG,GAGgBnB,EAAK,SAGzBU,OAAaG,KAElB,CAACb,iBAGAJ,gBAACyB,iBAAyBC,GAAIC,EAAYxB,IAAKA,GAASI,GACnDH,EAAKY,OAAS,GAAKH,eAChBb,6CACIA,wCACIA,sBAAI4B,UAAU,mBACTxB,EAAKyB,KAAKnB,gBACPV,sBAAIW,IAAKD,EAAIC,kBACTX,gBAAC8B,GACGF,0BAA2BlB,EAAIC,MAAQE,EAAUF,IAAM,YAAc,OACrEoB,QAAS,IAAMX,EAAgBV,GAC/BsB,aAAa,QAEZtB,EAAIuB,WAKpB5B,gBAA+BL,gCAAGK,iBAEvCL,gBAACkC,GAAMC,KAAK,YAAYC,UAAU,OAAOJ,aAAa,uBACtDhC,gBAACyB,GAAQC,GAAG,MAAME,2BAA2BV,EAAY,UAAY,KAChEL,EAAUwB,uBAInBrC"}
1
+ {"version":3,"file":"Tabs.js","sources":["../../../../src/components/Tabs/Tabs.tsx"],"sourcesContent":["import React, { useEffect } from \"react\";\n\nimport { Element } from \"../Element/Element\";\nimport { CommonAndHTMLProps } from \"../Element/constants\";\nimport { HRule } from \"../HRule/HRule\";\nimport { Text } from \"../Typography/Text\";\n\nimport { TabsStyled } from \"./Tabs.styled\";\n\n// prettier-ignore\ninterface TabType {\n key : string;\n label : React.ReactNode;\n content : React.ReactNode;\n}\n\n// prettier-ignore\nexport interface TabsCustomProps {\n tabs : TabType[];\n /** wrapper to render additional content inside the nav along with tab labels */\n additionalNavContentWrapper?: React.ReactNode; \n defaultActiveTab?: React.ReactNode;\n}\n\nexport type TabsElementType = HTMLDivElement;\nexport type TabsProps = Omit<CommonAndHTMLProps<TabsElementType>, keyof TabsCustomProps> & TabsCustomProps;\n\nexport const Tabs = React.forwardRef(\n ({ tabs, additionalNavContentWrapper, defaultActiveTab, ...props }: TabsProps, ref: React.Ref<TabsElementType>) => {\n const index = tabs.findIndex((tab) => tab.key === defaultActiveTab);\n const defaultTabIndex = index > -1 ? index : 0;\n const [activeTab, setActiveTab] = React.useState<TabType | undefined>(\n tabs.length > 0 ? tabs[defaultTabIndex] : undefined\n );\n const [isExiting, setIsExiting] = React.useState<boolean>(false);\n\n const handleTabChange = (tab: TabType) => {\n setIsExiting(true);\n setTimeout(() => {\n setIsExiting(false);\n setActiveTab(tab);\n }, 120);\n };\n\n useEffect(() => {\n if (tabs.length > 0) {\n const matchingTab = tabs.find((tab) => tab.key === activeTab?.key);\n if (matchingTab) {\n handleTabChange(matchingTab);\n } else {\n handleTabChange(tabs[0]);\n }\n } else {\n setActiveTab(undefined);\n }\n }, [tabs]);\n\n return (\n <Element<TabsElementType> as={TabsStyled} ref={ref} {...props}>\n {tabs.length > 0 && activeTab ? (\n <>\n <nav>\n <ul className=\"tab-labels-list\">\n {tabs.map((tab) => (\n <li key={tab.key}>\n <Text\n className={`is-clickable ${tab.key === activeTab.key ? \"is-active\" : \"\"} }`}\n onClick={() => handleTabChange(tab)}\n marginBottom=\"none\"\n >\n {tab.label}\n </Text>\n </li>\n ))}\n </ul>\n {additionalNavContentWrapper && <>{additionalNavContentWrapper}</>}\n </nav>\n <HRule kind=\"secondary\" marginTop=\"none\" marginBottom=\"micro\" />\n <Element as=\"div\" className={`tabs-content ${isExiting ? \"exiting\" : \"\"}`}>\n {activeTab.content}\n </Element>\n </>\n ) : (\n <></>\n )}\n </Element>\n );\n }\n);\n"],"names":["Tabs","React","forwardRef","_a","ref","tabs","additionalNavContentWrapper","defaultActiveTab","props","index","findIndex","tab","key","defaultTabIndex","activeTab","setActiveTab","useState","length","undefined","isExiting","setIsExiting","handleTabChange","setTimeout","useEffect","matchingTab","find","Element","as","TabsStyled","className","map","Text","onClick","marginBottom","label","HRule","kind","marginTop","content"],"mappings":"gSA2BaA,eAAOC,EAAMC,YACtB,CAACC,EAA8EC,SAA9EC,KAAEA,EAAFC,4BAAQA,EAARC,iBAAqCA,KAAqBC,MAA1D,iEACSC,EAAQJ,EAAKK,WAAWC,GAAQA,EAAIC,MAAQL,IAC5CM,EAAkBJ,GAAS,EAAIA,EAAQ,GACtCK,EAAWC,GAAgBd,EAAMe,SACpCX,EAAKY,OAAS,EAAIZ,EAAKQ,QAAmBK,IAEvCC,EAAWC,GAAgBnB,EAAMe,UAAkB,GAEpDK,EAAmBV,IACrBS,GAAa,GACbE,YAAW,KACPF,GAAa,GACbL,EAAaJ,KACd,aAGPY,GAAU,QACFlB,EAAKY,OAAS,EAAG,OACXO,EAAcnB,EAAKoB,MAAMd,GAAQA,EAAIC,OAAQE,MAAAA,SAAAA,EAAWF,OAE1DS,EADAG,GAGgBnB,EAAK,SAGzBU,OAAaG,KAElB,CAACb,iBAGAJ,gBAACyB,iBAAyBC,GAAIC,EAAYxB,IAAKA,GAASI,GACnDH,EAAKY,OAAS,GAAKH,eAChBb,6CACIA,wCACIA,sBAAI4B,UAAU,mBACTxB,EAAKyB,KAAKnB,gBACPV,sBAAIW,IAAKD,EAAIC,kBACTX,gBAAC8B,GACGF,0BAA2BlB,EAAIC,MAAQE,EAAUF,IAAM,YAAc,OACrEoB,QAAS,IAAMX,EAAgBV,GAC/BsB,aAAa,QAEZtB,EAAIuB,WAKpB5B,gBAA+BL,gCAAGK,iBAEvCL,gBAACkC,GAAMC,KAAK,YAAYC,UAAU,OAAOJ,aAAa,uBACtDhC,gBAACyB,GAAQC,GAAG,MAAME,2BAA2BV,EAAY,UAAY,KAChEL,EAAUwB,uBAInBrC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "fictoan-react",
3
- "version": "0.45.4",
3
+ "version": "0.46.5",
4
4
  "private": false,
5
5
  "main": "dist/cjs/index.js",
6
6
  "module": "dist/es/index.js",