@tounsoo/input-number-mask 1.0.7 → 1.0.9
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/README.md +1 -0
- package/dist/index.cjs +1 -1
- package/dist/index.cjs.map +1 -1
- package/dist/index.mjs +142 -103
- package/dist/index.mjs.map +1 -1
- package/package.json +15 -17
package/README.md
CHANGED
package/dist/index.cjs
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const
|
|
1
|
+
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const k=require("react"),S=e=>/\d/.test(e),E=(e,t)=>{let f="",n=0;for(let g=0;g<e.length;g++){const o=e[g];if(n>=t.length)break;if(t[n]==="d")S(o)&&(f+=o,n++);else if(t[n]===o)n++;else{let s=n;for(;s<t.length&&t[s]!=="d"&&t[s]!==o;)s++;s<t.length&&t[s]==="d"?S(o)&&(f+=o,n=s+1):s<t.length&&t[s]===o&&(n=s+1)}}return f},V=(e,t,f)=>{let n="",g=0;for(let o=0;o<t.length;o++)if(t[o]==="d"){if(g<e.length)n+=e[g++];else if(f&&o<f.length)n+=f[o];else if(!f)break}else n+=t[o];return n},K=(e,t,f,n,g,o)=>{let s=e,v=n;if(o===""){for(let d=n;d<g;d++)if(t[d]==="d"){const c=f&&d<f.length?f[d]:"_";s=s.substring(0,d)+c+s.substring(d+1)}return{value:s,cursor:n}}if(n!==g){let d=n;if(S(o)&&e[n]===o)for(d=n+1;d<g&&t[d]!=="d";)d++;for(let b=d;b<g;b++)if(t[b]==="d"){const x=f&&b<f.length?f[b]:"_";s=s.substring(0,b)+x+s.substring(b+1)}let c=d;for(;c<t.length&&t[c]!=="d";)c++;return c<t.length&&S(o)?(s=s.substring(0,c)+o+s.substring(c+1),v=c+1):v=n,{value:s,cursor:v}}let a=n;for(;a<t.length&&t[a]!=="d";)a++;return a<t.length&&S(o)?(s=s.substring(0,a)+o+s.substring(a+1),v=a+1):v=n,{value:s,cursor:v}};function N({template:e,placeholder:t,keepPosition:f=!1}){const[n,g]=k.useState(()=>V("",e,t)),[o,s]=k.useState(null),v=k.useRef(null),a=k.useRef({start:0,end:0});k.useLayoutEffect(()=>{v.current&&o!==null&&v.current.setSelectionRange(o,o)},[o,n]),k.useLayoutEffect(()=>{const c=v.current;if(!c)return;const b=()=>{c&&(a.current={start:c.selectionStart||0,end:c.selectionEnd||0})},x=C=>{b();const D=C.target,l=D.selectionStart||0,L=D.selectionEnd||0,I=l!==L;if(C.key==="Backspace"){if(l===0&&!I)return;C.preventDefault();let r=l-1;if(I)if(f){let u=n;for(let i=l;i<L;i++)if(e[i]==="d"){const w=t&&i<t.length?t[i]:"_";u=u.substring(0,i)+w+u.substring(i+1)}g(u),s(l);return}else{const u=n.slice(0,l)+n.slice(L),i=E(u,e),w=V(i,e,t);g(w),s(l);return}for(;r>=0&&e[r]!=="d";)r--;if(r<0)return;if(f){const u=t&&r<t.length?t[r]:"_",i=n.substring(0,r)+u+n.substring(r+1);g(i),s(r)}else{const u=n.slice(0,r)+n.slice(r+1),i=E(u,e),w=V(i,e,t);g(w),s(r)}}else if(C.key==="Delete"){if(C.preventDefault(),I)if(f){let u=n;for(let i=l;i<L;i++)if(e[i]==="d"){const w=t&&i<t.length?t[i]:"_";u=u.substring(0,i)+w+u.substring(i+1)}g(u),s(l);return}else{const u=n.slice(0,l)+n.slice(L),i=E(u,e),w=V(i,e,t);g(w),s(l);return}let r=l;for(;r<e.length&&e[r]!=="d";)r++;if(r>=n.length)return;if(f){const u=t&&r<t.length?t[r]:"_",i=n.substring(0,r)+u+n.substring(r+1);g(i),s(l)}else{const u=n.slice(0,r)+n.slice(r+1),i=E(u,e),w=V(i,e,t);g(w),s(l)}}},T=C=>{const D=C.target,l=D.value,L=D.selectionStart||0,I=()=>{const r=l.slice(0,L),u=E(r,e).length,i=E(l,e),w=V(i,e,t);g(w);let R=0,h=0;for(let y=0;y<w.length&&!(R>=u);y++)S(w[y])&&e[y]==="d"&&R++,h++;for(;h<w.length&&e[h]!=="d";)h++;s(h)};if(f){const r=a.current.start,u=a.current.end,i=r!==u,w=u-r;if(l.length-n.length+w===1&&L>0){const h=L-1,y=l[h];if(S(y)){const _=i?r:h,B=i?u:h;if(_<e.length){const M=K(n,e,t,_,B,y);g(M.value),s(M.cursor);return}}}}I()};return c.addEventListener("keydown",x),c.addEventListener("input",T),c.addEventListener("select",b),c.addEventListener("click",b),c.addEventListener("keyup",b),()=>{c.removeEventListener("keydown",x),c.removeEventListener("input",T),c.removeEventListener("select",b),c.removeEventListener("click",b),c.removeEventListener("keyup",b)}},[n,e,t,f]);const d=E(n,e);return{value:n,displayValue:n,rawValue:d,ref:v}}exports.useInputNumberMask=N;
|
|
2
2
|
//# sourceMappingURL=index.cjs.map
|
package/dist/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.cjs","sources":["../src/utils/maskUtils.ts","../src/useInputNumberMask.ts"],"sourcesContent":["export const isDigit = (char: string) => /\\d/.test(char);\n\nexport const cleanInput = (input: string, template: string): string => {\n let extracted = '';\n let t = 0;\n\n for (let i = 0; i < input.length; i++) {\n const char = input[i];\n\n if (t >= template.length) {\n break;\n }\n\n if (template[t] === 'd') {\n if (isDigit(char)) {\n extracted += char;\n t++;\n } else {\n // Ignore placeholders or garbage\n }\n } else if (template[t] === char) {\n t++;\n } else {\n let nextT = t;\n while (nextT < template.length && template[nextT] !== 'd' && template[nextT] !== char) {\n nextT++;\n }\n if (nextT < template.length && template[nextT] === 'd') {\n if (isDigit(char)) {\n extracted += char;\n t = nextT + 1;\n }\n } else if (nextT < template.length && template[nextT] === char) {\n t = nextT + 1;\n }\n }\n }\n return extracted;\n};\n\nexport const formatWithMask = (digits: string, template: string, placeholder?: string): string => {\n let res = '';\n let dIdx = 0;\n\n for (let i = 0; i < template.length; i++) {\n const isSlot = template[i] === 'd';\n\n if (isSlot) {\n if (dIdx < digits.length) {\n res += digits[dIdx++];\n } else {\n // Empty slot\n if (placeholder && i < placeholder.length) {\n res += placeholder[i];\n } else {\n if (!placeholder) {\n break;\n }\n }\n }\n } else {\n res += template[i];\n }\n }\n return res;\n};\n","import { useRef, useState, useLayoutEffect } from 'react';\nimport { cleanInput, formatWithMask, isDigit } from './utils/maskUtils';\n\nexport interface UseInputNumberMaskProps {\n /**\n * The mask template. 'd' represents a digit slot.\n * All other characters are treated as literals.\n * @example \"+1 (ddd) ddd-dddd\" for US phone numbers\n */\n template: string;\n\n /**\n * Optional full-length placeholder shown in empty slots.\n * Should match the template length.\n * @example \"mm/dd/yyyy\" for a date mask\n */\n placeholder?: string;\n\n /**\n * If true, deletion replaces with placeholder char\n * instead of shifting subsequent digits left.\n * @default false\n */\n keepPosition?: boolean;\n}\n\nexport interface UseInputNumberMaskReturn {\n /** \n * The raw value with formatting applied \n * @example \"+1 (234) 567-8900\" \n */\n value: string;\n /** \n * Deprecated: alias for value. The value to display in the input. \n * @example \"+1 (234) 567-8900\" \n */\n displayValue: string;\n /** \n * The unmasked digits only \n * @example \"1234567890\" \n */\n rawValue: string;\n /** \n * Ref to be attached to the HTML input element \n */\n ref: React.RefObject<HTMLInputElement | null>;\n}\n\nexport function useInputNumberMask({\n template,\n placeholder,\n keepPosition = false,\n}: UseInputNumberMaskProps): UseInputNumberMaskReturn {\n\n const [value, setValue] = useState(() => formatWithMask('', template, placeholder));\n const [cursor, setCursor] = useState<number | null>(null);\n const ref = useRef<HTMLInputElement>(null);\n\n useLayoutEffect(() => {\n if (ref.current && cursor !== null) {\n ref.current.setSelectionRange(cursor, cursor);\n }\n }, [cursor, value]);\n\n // Ref-based Event Handling\n useLayoutEffect(() => {\n const input = ref.current;\n if (!input) return;\n\n const handleKeyDown = (e: globalThis.KeyboardEvent) => {\n const el = e.target as HTMLInputElement;\n const start = el.selectionStart || 0;\n const end = el.selectionEnd || 0;\n const isSelection = start !== end;\n\n if (e.key === 'Backspace') {\n if (start === 0 && !isSelection) return;\n // We prevent default to handle the state update manually\n e.preventDefault();\n\n let deleteIndex = start - 1;\n\n if (isSelection) {\n if (keepPosition) {\n // Replace range with placeholders\n let newVal = value;\n for (let i = start; i < end; i++) {\n if (template[i] === 'd') {\n const pChar = placeholder && i < placeholder.length ? placeholder[i] : '_';\n newVal = newVal.substring(0, i) + pChar + newVal.substring(i + 1);\n }\n }\n setValue(newVal);\n setCursor(start);\n return;\n } else {\n // Standard delete range (shift)\n const newValRaw = value.slice(0, start) + value.slice(end);\n const newDigits = cleanInput(newValRaw, template);\n const formatted = formatWithMask(newDigits, template, placeholder);\n setValue(formatted);\n setCursor(start);\n return;\n }\n }\n\n // Single char backspace\n while (deleteIndex >= 0) {\n const isTemplateLiteral = template[deleteIndex] !== 'd';\n if (isTemplateLiteral) {\n deleteIndex--;\n } else {\n break;\n }\n }\n\n if (deleteIndex < 0) return; // Nothing to delete\n\n if (keepPosition) {\n const pChar = placeholder && deleteIndex < placeholder.length ? placeholder[deleteIndex] : '_';\n const newVal = value.substring(0, deleteIndex) + pChar + value.substring(deleteIndex + 1);\n setValue(newVal);\n setCursor(deleteIndex);\n } else {\n // Shift behavior\n const newValRaw = value.slice(0, deleteIndex) + value.slice(deleteIndex + 1);\n const newDigits = cleanInput(newValRaw, template);\n const formatted = formatWithMask(newDigits, template, placeholder);\n setValue(formatted);\n setCursor(deleteIndex);\n }\n } else if (e.key === 'Delete') {\n e.preventDefault();\n // Delete forward\n if (isSelection) {\n if (keepPosition) {\n let newVal = value;\n for (let i = start; i < end; i++) {\n if (template[i] === 'd') {\n const pChar = placeholder && i < placeholder.length ? placeholder[i] : '_';\n newVal = newVal.substring(0, i) + pChar + newVal.substring(i + 1);\n }\n }\n setValue(newVal);\n setCursor(start);\n return;\n } else {\n const newValRaw = value.slice(0, start) + value.slice(end);\n const newDigits = cleanInput(newValRaw, template);\n const formatted = formatWithMask(newDigits, template, placeholder);\n setValue(formatted);\n setCursor(start);\n return;\n }\n }\n\n // Single char delete\n let deleteIndex = start;\n while (deleteIndex < template.length) {\n const isTemplateLiteral = template[deleteIndex] !== 'd';\n if (isTemplateLiteral) {\n deleteIndex++;\n } else {\n break;\n }\n }\n\n if (deleteIndex >= value.length) return;\n\n if (keepPosition) {\n const pChar = placeholder && deleteIndex < placeholder.length ? placeholder[deleteIndex] : '_';\n const newVal = value.substring(0, deleteIndex) + pChar + value.substring(deleteIndex + 1);\n setValue(newVal);\n setCursor(start);\n } else {\n // Shift behavior\n const newValRaw = value.slice(0, deleteIndex) + value.slice(deleteIndex + 1);\n const newDigits = cleanInput(newValRaw, template);\n const formatted = formatWithMask(newDigits, template, placeholder);\n setValue(formatted);\n setCursor(start);\n }\n }\n };\n\n const handleInput = (e: Event) => {\n // We cast to InputEvent or basic Event. target is HTMLInputElement.\n const target = e.target as HTMLInputElement;\n const inputVal = target.value;\n const rawCursor = target.selectionStart || 0;\n\n // Standard behavior logic\n const standardChange = () => {\n const beforeCursor = inputVal.slice(0, rawCursor);\n const digitsBeforeCursor = cleanInput(beforeCursor, template).length;\n\n const newDigits = cleanInput(inputVal, template);\n const newFormatted = formatWithMask(newDigits, template, placeholder);\n\n setValue(newFormatted);\n\n let currentDigits = 0;\n let newCursor = 0;\n for (let i = 0; i < newFormatted.length; i++) {\n if (currentDigits >= digitsBeforeCursor) {\n break;\n }\n if (isDigit(newFormatted[i]) && template[i] === 'd') {\n currentDigits++;\n }\n newCursor++;\n }\n\n while (newCursor < newFormatted.length && template[newCursor] !== 'd') {\n newCursor++;\n }\n\n setCursor(newCursor);\n };\n\n if (keepPosition) {\n // Measure diff against current state `value` (mapped in closure)\n // NOTE: `value` in this closure is stale if not included in dependency array.\n // We need strict dependency on `value`.\n if (inputVal.length > value.length) {\n const insertIndex = rawCursor - 1;\n const char = inputVal[insertIndex];\n\n if (!isDigit(char)) {\n standardChange();\n return;\n }\n\n let targetIndex = insertIndex;\n while (targetIndex < template.length) {\n if (template[targetIndex] === 'd') {\n break;\n }\n targetIndex++;\n }\n\n if (targetIndex >= template.length) {\n standardChange();\n return;\n }\n\n const newValue = value.substring(0, targetIndex) + char + value.substring(targetIndex + 1);\n\n setValue(newValue);\n setCursor(targetIndex + 1);\n return;\n }\n }\n\n standardChange();\n };\n\n input.addEventListener('keydown', handleKeyDown);\n input.addEventListener('input', handleInput);\n\n return () => {\n input.removeEventListener('keydown', handleKeyDown);\n input.removeEventListener('input', handleInput);\n };\n }, [value, template, placeholder, keepPosition]); // Re-bind when value changes to have fresh closure\n\n const rawValue = cleanInput(value, template);\n\n return {\n value,\n displayValue: value,\n rawValue,\n ref\n };\n}\n"],"names":["isDigit","char","cleanInput","input","template","extracted","t","i","nextT","formatWithMask","digits","placeholder","res","dIdx","useInputNumberMask","keepPosition","value","setValue","useState","cursor","setCursor","ref","useRef","useLayoutEffect","handleKeyDown","e","el","start","end","isSelection","deleteIndex","newVal","pChar","newValRaw","newDigits","formatted","handleInput","target","inputVal","rawCursor","standardChange","beforeCursor","digitsBeforeCursor","newFormatted","currentDigits","newCursor","insertIndex","targetIndex","newValue","rawValue"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.cjs","sources":["../src/utils/maskUtils.ts","../src/useInputNumberMask.ts"],"sourcesContent":["export const isDigit = (char: string) => /\\d/.test(char);\n\nexport const cleanInput = (input: string, template: string): string => {\n let extracted = '';\n let t = 0;\n\n for (let i = 0; i < input.length; i++) {\n const char = input[i];\n\n if (t >= template.length) {\n break;\n }\n\n if (template[t] === 'd') {\n if (isDigit(char)) {\n extracted += char;\n t++;\n } else {\n // Ignore placeholders or garbage\n }\n } else if (template[t] === char) {\n t++;\n } else {\n let nextT = t;\n while (nextT < template.length && template[nextT] !== 'd' && template[nextT] !== char) {\n nextT++;\n }\n if (nextT < template.length && template[nextT] === 'd') {\n if (isDigit(char)) {\n extracted += char;\n t = nextT + 1;\n }\n } else if (nextT < template.length && template[nextT] === char) {\n t = nextT + 1;\n }\n }\n }\n return extracted;\n};\n\nexport const formatWithMask = (digits: string, template: string, placeholder?: string): string => {\n let res = '';\n let dIdx = 0;\n\n for (let i = 0; i < template.length; i++) {\n const isSlot = template[i] === 'd';\n\n if (isSlot) {\n if (dIdx < digits.length) {\n res += digits[dIdx++];\n } else {\n // Empty slot\n if (placeholder && i < placeholder.length) {\n res += placeholder[i];\n } else {\n if (!placeholder) {\n break;\n }\n }\n }\n } else {\n res += template[i];\n }\n }\n return res;\n};\n\nexport const applyKeepPositionChange = (\n value: string,\n template: string,\n placeholder: string | undefined,\n start: number,\n end: number,\n char: string\n): { value: string; cursor: number } => {\n let newValue = value;\n let newCursor = start;\n\n // Deletion\n if (char === '') {\n for (let i = start; i < end; i++) {\n if (template[i] === 'd') {\n const pChar = placeholder && i < placeholder.length ? placeholder[i] : '_';\n newValue = newValue.substring(0, i) + pChar + newValue.substring(i + 1);\n }\n }\n return { value: newValue, cursor: start };\n }\n\n // Replacement (Selection)\n if (start !== end) {\n // 1. Replace the first digit slot at start (if it's a digit slot)\n // Actually, if we selected a range, we typically want to replace the *entire* range content\n // with the new char *at the beginning*, and clear the rest.\n // But we must respect the template chars.\n\n // Strategy:\n // - Clear the entire range first (replace with placeholders)\n // - Insert the char at the first available digit slot within the range (or shortly after if range starts on literal?)\n // Wait, standard input behavior replacing a selection \"12/34\" with \"5\":\n // \"12/5...\"\n // So effectively: delete range, then insert char.\n\n // Let's implement it as:\n // 1. Check if first char of selection matches the typed char\n // If so, skip it and insert at the next digit slot\n let insertStart = start;\n if (isDigit(char) && value[start] === char) {\n // The first position already has the same digit, so skip it\n insertStart = start + 1;\n // Skip any literals to find the next digit slot\n while (insertStart < end && template[insertStart] !== 'd') {\n insertStart++;\n }\n }\n\n // 2. Clear the range from insertStart to end (fill with placeholders)\n for (let i = insertStart; i < end; i++) {\n if (template[i] === 'd') {\n const pChar = placeholder && i < placeholder.length ? placeholder[i] : '_';\n newValue = newValue.substring(0, i) + pChar + newValue.substring(i + 1);\n }\n }\n\n // 3. Insert the char at insertStart (finding closest digit slot)\n let targetIndex = insertStart;\n while (targetIndex < template.length) {\n if (template[targetIndex] === 'd') {\n break;\n }\n targetIndex++;\n }\n\n if (targetIndex < template.length && isDigit(char)) {\n newValue = newValue.substring(0, targetIndex) + char + newValue.substring(targetIndex + 1);\n newCursor = targetIndex + 1;\n } else {\n newCursor = start;\n }\n\n return { value: newValue, cursor: newCursor };\n }\n\n // Insertion (No selection)\n // Find next digit slot\n let targetIndex = start;\n while (targetIndex < template.length) {\n if (template[targetIndex] === 'd') {\n break;\n }\n targetIndex++;\n }\n\n if (targetIndex < template.length && isDigit(char)) {\n newValue = newValue.substring(0, targetIndex) + char + newValue.substring(targetIndex + 1);\n newCursor = targetIndex + 1;\n } else {\n newCursor = start; // No change\n }\n\n return { value: newValue, cursor: newCursor };\n};\n","import { useRef, useState, useLayoutEffect } from 'react';\nimport { cleanInput, formatWithMask, isDigit, applyKeepPositionChange } from './utils/maskUtils';\n\nexport interface UseInputNumberMaskProps {\n /**\n * The mask template. 'd' represents a digit slot.\n * All other characters are treated as literals.\n * @example \"+1 (ddd) ddd-dddd\" for US phone numbers\n */\n template: string;\n\n /**\n * Optional full-length placeholder shown in empty slots.\n * Should match the template length.\n * @example \"mm/dd/yyyy\" for a date mask\n */\n placeholder?: string;\n\n /**\n * If true, deletion replaces with placeholder char\n * instead of shifting subsequent digits left.\n * @default false\n */\n keepPosition?: boolean;\n}\n\nexport interface UseInputNumberMaskReturn {\n /** \n * The raw value with formatting applied \n * @example \"+1 (234) 567-8900\" \n */\n value: string;\n /** \n * Deprecated: alias for value. The value to display in the input. \n * @example \"+1 (234) 567-8900\" \n */\n displayValue: string;\n /** \n * The unmasked digits only \n * @example \"1234567890\" \n */\n rawValue: string;\n /** \n * Ref to be attached to the HTML input element \n */\n ref: React.RefObject<HTMLInputElement | null>;\n}\n\nexport function useInputNumberMask({\n template,\n placeholder,\n keepPosition = false,\n}: UseInputNumberMaskProps): UseInputNumberMaskReturn {\n\n const [value, setValue] = useState(() => formatWithMask('', template, placeholder));\n const [cursor, setCursor] = useState<number | null>(null);\n const ref = useRef<HTMLInputElement>(null);\n const selectionRef = useRef<{ start: number; end: number }>({ start: 0, end: 0 });\n\n useLayoutEffect(() => {\n if (ref.current && cursor !== null) {\n ref.current.setSelectionRange(cursor, cursor);\n }\n }, [cursor, value]);\n\n // Ref-based Event Handling\n useLayoutEffect(() => {\n const input = ref.current;\n if (!input) return;\n\n const updateSelection = () => {\n if (input) {\n selectionRef.current = {\n start: input.selectionStart || 0,\n end: input.selectionEnd || 0,\n };\n }\n };\n\n const handleKeyDown = (e: globalThis.KeyboardEvent) => {\n updateSelection();\n const el = e.target as HTMLInputElement;\n const start = el.selectionStart || 0;\n const end = el.selectionEnd || 0;\n const isSelection = start !== end;\n\n if (e.key === 'Backspace') {\n if (start === 0 && !isSelection) return;\n // We prevent default to handle the state update manually\n e.preventDefault();\n\n let deleteIndex = start - 1;\n\n if (isSelection) {\n if (keepPosition) {\n // Replace range with placeholders\n let newVal = value;\n for (let i = start; i < end; i++) {\n if (template[i] === 'd') {\n const pChar = placeholder && i < placeholder.length ? placeholder[i] : '_';\n newVal = newVal.substring(0, i) + pChar + newVal.substring(i + 1);\n }\n }\n setValue(newVal);\n setCursor(start);\n return;\n } else {\n // Standard delete range (shift)\n const newValRaw = value.slice(0, start) + value.slice(end);\n const newDigits = cleanInput(newValRaw, template);\n const formatted = formatWithMask(newDigits, template, placeholder);\n setValue(formatted);\n setCursor(start);\n return;\n }\n }\n\n // Single char backspace\n while (deleteIndex >= 0) {\n const isTemplateLiteral = template[deleteIndex] !== 'd';\n if (isTemplateLiteral) {\n deleteIndex--;\n } else {\n break;\n }\n }\n\n if (deleteIndex < 0) return; // Nothing to delete\n\n if (keepPosition) {\n const pChar = placeholder && deleteIndex < placeholder.length ? placeholder[deleteIndex] : '_';\n const newVal = value.substring(0, deleteIndex) + pChar + value.substring(deleteIndex + 1);\n setValue(newVal);\n setCursor(deleteIndex);\n } else {\n // Shift behavior\n const newValRaw = value.slice(0, deleteIndex) + value.slice(deleteIndex + 1);\n const newDigits = cleanInput(newValRaw, template);\n const formatted = formatWithMask(newDigits, template, placeholder);\n setValue(formatted);\n setCursor(deleteIndex);\n }\n } else if (e.key === 'Delete') {\n e.preventDefault();\n // Delete forward\n if (isSelection) {\n if (keepPosition) {\n let newVal = value;\n for (let i = start; i < end; i++) {\n if (template[i] === 'd') {\n const pChar = placeholder && i < placeholder.length ? placeholder[i] : '_';\n newVal = newVal.substring(0, i) + pChar + newVal.substring(i + 1);\n }\n }\n setValue(newVal);\n setCursor(start);\n return;\n } else {\n const newValRaw = value.slice(0, start) + value.slice(end);\n const newDigits = cleanInput(newValRaw, template);\n const formatted = formatWithMask(newDigits, template, placeholder);\n setValue(formatted);\n setCursor(start);\n return;\n }\n }\n\n // Single char delete\n let deleteIndex = start;\n while (deleteIndex < template.length) {\n const isTemplateLiteral = template[deleteIndex] !== 'd';\n if (isTemplateLiteral) {\n deleteIndex++;\n } else {\n break;\n }\n }\n\n if (deleteIndex >= value.length) return;\n\n if (keepPosition) {\n const pChar = placeholder && deleteIndex < placeholder.length ? placeholder[deleteIndex] : '_';\n const newVal = value.substring(0, deleteIndex) + pChar + value.substring(deleteIndex + 1);\n setValue(newVal);\n setCursor(start);\n } else {\n // Shift behavior\n const newValRaw = value.slice(0, deleteIndex) + value.slice(deleteIndex + 1);\n const newDigits = cleanInput(newValRaw, template);\n const formatted = formatWithMask(newDigits, template, placeholder);\n setValue(formatted);\n setCursor(start);\n }\n }\n };\n\n const handleInput = (e: Event) => {\n // We cast to InputEvent or basic Event. target is HTMLInputElement.\n const target = e.target as HTMLInputElement;\n const inputVal = target.value;\n const rawCursor = target.selectionStart || 0;\n\n // Standard behavior logic\n const standardChange = () => {\n const beforeCursor = inputVal.slice(0, rawCursor);\n const digitsBeforeCursor = cleanInput(beforeCursor, template).length;\n\n const newDigits = cleanInput(inputVal, template);\n const newFormatted = formatWithMask(newDigits, template, placeholder);\n\n setValue(newFormatted);\n\n let currentDigits = 0;\n let newCursor = 0;\n for (let i = 0; i < newFormatted.length; i++) {\n if (currentDigits >= digitsBeforeCursor) {\n break;\n }\n if (isDigit(newFormatted[i]) && template[i] === 'd') {\n currentDigits++;\n }\n newCursor++;\n }\n\n while (newCursor < newFormatted.length && template[newCursor] !== 'd') {\n newCursor++;\n }\n\n setCursor(newCursor);\n };\n\n if (keepPosition) {\n const prevStart = selectionRef.current.start;\n const prevEnd = selectionRef.current.end;\n const wasSelection = prevStart !== prevEnd;\n const selectionLength = prevEnd - prevStart;\n const typedChars = inputVal.length - value.length + selectionLength;\n\n // Handle single character input (typing one digit)\n if (typedChars === 1 && rawCursor > 0) {\n const insertIndex = rawCursor - 1;\n const char = inputVal[insertIndex];\n\n if (isDigit(char)) {\n // For selection replacement, use the selection range\n // For cursor insertion (no selection), use the cursor position\n const startPos = wasSelection ? prevStart : insertIndex;\n const endPos = wasSelection ? prevEnd : insertIndex;\n\n // Only apply keepPosition if within template bounds\n // If cursor is at/beyond template length (e.g., typing at end),\n // fall through to standardChange() for normal fill behavior\n if (startPos < template.length) {\n const result = applyKeepPositionChange(\n value,\n template,\n placeholder,\n startPos,\n endPos,\n char\n );\n\n setValue(result.value);\n setCursor(result.cursor);\n return;\n }\n }\n }\n }\n\n standardChange();\n };\n\n input.addEventListener('keydown', handleKeyDown);\n input.addEventListener('input', handleInput);\n input.addEventListener('select', updateSelection);\n input.addEventListener('click', updateSelection);\n input.addEventListener('keyup', updateSelection);\n\n return () => {\n input.removeEventListener('keydown', handleKeyDown);\n input.removeEventListener('input', handleInput);\n input.removeEventListener('select', updateSelection);\n input.removeEventListener('click', updateSelection);\n input.removeEventListener('keyup', updateSelection);\n };\n }, [value, template, placeholder, keepPosition]); // Re-bind when value changes to have fresh closure\n\n const rawValue = cleanInput(value, template);\n\n return {\n value,\n displayValue: value,\n rawValue,\n ref\n };\n}\n"],"names":["isDigit","char","cleanInput","input","template","extracted","t","i","nextT","formatWithMask","digits","placeholder","res","dIdx","applyKeepPositionChange","value","start","end","newValue","newCursor","pChar","insertStart","targetIndex","useInputNumberMask","keepPosition","setValue","useState","cursor","setCursor","ref","useRef","selectionRef","useLayoutEffect","updateSelection","handleKeyDown","e","el","isSelection","deleteIndex","newVal","newValRaw","newDigits","formatted","handleInput","target","inputVal","rawCursor","standardChange","beforeCursor","digitsBeforeCursor","newFormatted","currentDigits","prevStart","prevEnd","wasSelection","selectionLength","insertIndex","startPos","endPos","result","rawValue"],"mappings":"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"}
|
package/dist/index.mjs
CHANGED
|
@@ -1,149 +1,188 @@
|
|
|
1
|
-
import { useState as
|
|
2
|
-
const
|
|
3
|
-
let
|
|
1
|
+
import { useState as B, useRef as K, useLayoutEffect as M } from "react";
|
|
2
|
+
const V = (e) => /\d/.test(e), k = (e, s) => {
|
|
3
|
+
let f = "", n = 0;
|
|
4
4
|
for (let g = 0; g < e.length; g++) {
|
|
5
|
-
const
|
|
6
|
-
if (n >=
|
|
5
|
+
const o = e[g];
|
|
6
|
+
if (n >= s.length)
|
|
7
7
|
break;
|
|
8
|
-
if (
|
|
9
|
-
|
|
10
|
-
else if (
|
|
8
|
+
if (s[n] === "d")
|
|
9
|
+
V(o) && (f += o, n++);
|
|
10
|
+
else if (s[n] === o)
|
|
11
11
|
n++;
|
|
12
12
|
else {
|
|
13
|
-
let
|
|
14
|
-
for (;
|
|
15
|
-
|
|
16
|
-
|
|
13
|
+
let t = n;
|
|
14
|
+
for (; t < s.length && s[t] !== "d" && s[t] !== o; )
|
|
15
|
+
t++;
|
|
16
|
+
t < s.length && s[t] === "d" ? V(o) && (f += o, n = t + 1) : t < s.length && s[t] === o && (n = t + 1);
|
|
17
17
|
}
|
|
18
18
|
}
|
|
19
|
-
return
|
|
20
|
-
},
|
|
19
|
+
return f;
|
|
20
|
+
}, y = (e, s, f) => {
|
|
21
21
|
let n = "", g = 0;
|
|
22
|
-
for (let
|
|
23
|
-
if (
|
|
22
|
+
for (let o = 0; o < s.length; o++)
|
|
23
|
+
if (s[o] === "d") {
|
|
24
24
|
if (g < e.length)
|
|
25
25
|
n += e[g++];
|
|
26
|
-
else if (
|
|
27
|
-
n +=
|
|
28
|
-
else if (!
|
|
26
|
+
else if (f && o < f.length)
|
|
27
|
+
n += f[o];
|
|
28
|
+
else if (!f)
|
|
29
29
|
break;
|
|
30
30
|
} else
|
|
31
|
-
n +=
|
|
31
|
+
n += s[o];
|
|
32
32
|
return n;
|
|
33
|
+
}, N = (e, s, f, n, g, o) => {
|
|
34
|
+
let t = e, v = n;
|
|
35
|
+
if (o === "") {
|
|
36
|
+
for (let d = n; d < g; d++)
|
|
37
|
+
if (s[d] === "d") {
|
|
38
|
+
const c = f && d < f.length ? f[d] : "_";
|
|
39
|
+
t = t.substring(0, d) + c + t.substring(d + 1);
|
|
40
|
+
}
|
|
41
|
+
return { value: t, cursor: n };
|
|
42
|
+
}
|
|
43
|
+
if (n !== g) {
|
|
44
|
+
let d = n;
|
|
45
|
+
if (V(o) && e[n] === o)
|
|
46
|
+
for (d = n + 1; d < g && s[d] !== "d"; )
|
|
47
|
+
d++;
|
|
48
|
+
for (let b = d; b < g; b++)
|
|
49
|
+
if (s[b] === "d") {
|
|
50
|
+
const x = f && b < f.length ? f[b] : "_";
|
|
51
|
+
t = t.substring(0, b) + x + t.substring(b + 1);
|
|
52
|
+
}
|
|
53
|
+
let c = d;
|
|
54
|
+
for (; c < s.length && s[c] !== "d"; )
|
|
55
|
+
c++;
|
|
56
|
+
return c < s.length && V(o) ? (t = t.substring(0, c) + o + t.substring(c + 1), v = c + 1) : v = n, { value: t, cursor: v };
|
|
57
|
+
}
|
|
58
|
+
let h = n;
|
|
59
|
+
for (; h < s.length && s[h] !== "d"; )
|
|
60
|
+
h++;
|
|
61
|
+
return h < s.length && V(o) ? (t = t.substring(0, h) + o + t.substring(h + 1), v = h + 1) : v = n, { value: t, cursor: v };
|
|
33
62
|
};
|
|
34
|
-
function
|
|
63
|
+
function j({
|
|
35
64
|
template: e,
|
|
36
|
-
placeholder:
|
|
37
|
-
keepPosition:
|
|
65
|
+
placeholder: s,
|
|
66
|
+
keepPosition: f = !1
|
|
38
67
|
}) {
|
|
39
|
-
const [n, g] =
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
}, [
|
|
43
|
-
const
|
|
44
|
-
if (!
|
|
45
|
-
const
|
|
46
|
-
|
|
68
|
+
const [n, g] = B(() => y("", e, s)), [o, t] = B(null), v = K(null), h = K({ start: 0, end: 0 });
|
|
69
|
+
M(() => {
|
|
70
|
+
v.current && o !== null && v.current.setSelectionRange(o, o);
|
|
71
|
+
}, [o, n]), M(() => {
|
|
72
|
+
const c = v.current;
|
|
73
|
+
if (!c) return;
|
|
74
|
+
const b = () => {
|
|
75
|
+
c && (h.current = {
|
|
76
|
+
start: c.selectionStart || 0,
|
|
77
|
+
end: c.selectionEnd || 0
|
|
78
|
+
});
|
|
79
|
+
}, x = (a) => {
|
|
80
|
+
b();
|
|
81
|
+
const S = a.target, l = S.selectionStart || 0, C = S.selectionEnd || 0, D = l !== C;
|
|
47
82
|
if (a.key === "Backspace") {
|
|
48
|
-
if (
|
|
83
|
+
if (l === 0 && !D) return;
|
|
49
84
|
a.preventDefault();
|
|
50
|
-
let
|
|
51
|
-
if (
|
|
52
|
-
if (
|
|
53
|
-
let
|
|
54
|
-
for (let
|
|
55
|
-
if (e[
|
|
56
|
-
const
|
|
57
|
-
|
|
85
|
+
let r = l - 1;
|
|
86
|
+
if (D)
|
|
87
|
+
if (f) {
|
|
88
|
+
let u = n;
|
|
89
|
+
for (let i = l; i < C; i++)
|
|
90
|
+
if (e[i] === "d") {
|
|
91
|
+
const w = s && i < s.length ? s[i] : "_";
|
|
92
|
+
u = u.substring(0, i) + w + u.substring(i + 1);
|
|
58
93
|
}
|
|
59
|
-
g(
|
|
94
|
+
g(u), t(l);
|
|
60
95
|
return;
|
|
61
96
|
} else {
|
|
62
|
-
const
|
|
63
|
-
g(
|
|
97
|
+
const u = n.slice(0, l) + n.slice(C), i = k(u, e), w = y(i, e, s);
|
|
98
|
+
g(w), t(l);
|
|
64
99
|
return;
|
|
65
100
|
}
|
|
66
|
-
for (;
|
|
67
|
-
|
|
68
|
-
if (
|
|
69
|
-
if (
|
|
70
|
-
const
|
|
71
|
-
g(
|
|
101
|
+
for (; r >= 0 && e[r] !== "d"; )
|
|
102
|
+
r--;
|
|
103
|
+
if (r < 0) return;
|
|
104
|
+
if (f) {
|
|
105
|
+
const u = s && r < s.length ? s[r] : "_", i = n.substring(0, r) + u + n.substring(r + 1);
|
|
106
|
+
g(i), t(r);
|
|
72
107
|
} else {
|
|
73
|
-
const
|
|
74
|
-
g(
|
|
108
|
+
const u = n.slice(0, r) + n.slice(r + 1), i = k(u, e), w = y(i, e, s);
|
|
109
|
+
g(w), t(r);
|
|
75
110
|
}
|
|
76
111
|
} else if (a.key === "Delete") {
|
|
77
|
-
if (a.preventDefault(),
|
|
78
|
-
if (
|
|
79
|
-
let
|
|
80
|
-
for (let
|
|
81
|
-
if (e[
|
|
82
|
-
const
|
|
83
|
-
|
|
112
|
+
if (a.preventDefault(), D)
|
|
113
|
+
if (f) {
|
|
114
|
+
let u = n;
|
|
115
|
+
for (let i = l; i < C; i++)
|
|
116
|
+
if (e[i] === "d") {
|
|
117
|
+
const w = s && i < s.length ? s[i] : "_";
|
|
118
|
+
u = u.substring(0, i) + w + u.substring(i + 1);
|
|
84
119
|
}
|
|
85
|
-
g(
|
|
120
|
+
g(u), t(l);
|
|
86
121
|
return;
|
|
87
122
|
} else {
|
|
88
|
-
const
|
|
89
|
-
g(
|
|
123
|
+
const u = n.slice(0, l) + n.slice(C), i = k(u, e), w = y(i, e, s);
|
|
124
|
+
g(w), t(l);
|
|
90
125
|
return;
|
|
91
126
|
}
|
|
92
|
-
let
|
|
93
|
-
for (;
|
|
94
|
-
|
|
95
|
-
if (
|
|
96
|
-
if (
|
|
97
|
-
const
|
|
98
|
-
g(
|
|
127
|
+
let r = l;
|
|
128
|
+
for (; r < e.length && e[r] !== "d"; )
|
|
129
|
+
r++;
|
|
130
|
+
if (r >= n.length) return;
|
|
131
|
+
if (f) {
|
|
132
|
+
const u = s && r < s.length ? s[r] : "_", i = n.substring(0, r) + u + n.substring(r + 1);
|
|
133
|
+
g(i), t(l);
|
|
99
134
|
} else {
|
|
100
|
-
const
|
|
101
|
-
g(
|
|
135
|
+
const u = n.slice(0, r) + n.slice(r + 1), i = k(u, e), w = y(i, e, s);
|
|
136
|
+
g(w), t(l);
|
|
102
137
|
}
|
|
103
138
|
}
|
|
104
|
-
},
|
|
105
|
-
const
|
|
106
|
-
const
|
|
107
|
-
g(
|
|
108
|
-
let
|
|
109
|
-
for (let
|
|
110
|
-
|
|
111
|
-
for (;
|
|
112
|
-
|
|
113
|
-
|
|
139
|
+
}, R = (a) => {
|
|
140
|
+
const S = a.target, l = S.value, C = S.selectionStart || 0, D = () => {
|
|
141
|
+
const r = l.slice(0, C), u = k(r, e).length, i = k(l, e), w = y(i, e, s);
|
|
142
|
+
g(w);
|
|
143
|
+
let I = 0, L = 0;
|
|
144
|
+
for (let E = 0; E < w.length && !(I >= u); E++)
|
|
145
|
+
V(w[E]) && e[E] === "d" && I++, L++;
|
|
146
|
+
for (; L < w.length && e[L] !== "d"; )
|
|
147
|
+
L++;
|
|
148
|
+
t(L);
|
|
114
149
|
};
|
|
115
|
-
if (
|
|
116
|
-
const
|
|
117
|
-
if (
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
150
|
+
if (f) {
|
|
151
|
+
const r = h.current.start, u = h.current.end, i = r !== u, w = u - r;
|
|
152
|
+
if (l.length - n.length + w === 1 && C > 0) {
|
|
153
|
+
const L = C - 1, E = l[L];
|
|
154
|
+
if (V(E)) {
|
|
155
|
+
const _ = i ? r : L, F = i ? u : L;
|
|
156
|
+
if (_ < e.length) {
|
|
157
|
+
const T = N(
|
|
158
|
+
n,
|
|
159
|
+
e,
|
|
160
|
+
s,
|
|
161
|
+
_,
|
|
162
|
+
F,
|
|
163
|
+
E
|
|
164
|
+
);
|
|
165
|
+
g(T.value), t(T.cursor);
|
|
166
|
+
return;
|
|
167
|
+
}
|
|
168
|
+
}
|
|
127
169
|
}
|
|
128
|
-
const o = n.substring(0, s) + r + n.substring(s + 1);
|
|
129
|
-
g(o), u(s + 1);
|
|
130
|
-
return;
|
|
131
170
|
}
|
|
132
|
-
|
|
171
|
+
D();
|
|
133
172
|
};
|
|
134
|
-
return
|
|
135
|
-
|
|
173
|
+
return c.addEventListener("keydown", x), c.addEventListener("input", R), c.addEventListener("select", b), c.addEventListener("click", b), c.addEventListener("keyup", b), () => {
|
|
174
|
+
c.removeEventListener("keydown", x), c.removeEventListener("input", R), c.removeEventListener("select", b), c.removeEventListener("click", b), c.removeEventListener("keyup", b);
|
|
136
175
|
};
|
|
137
|
-
}, [n, e,
|
|
138
|
-
const
|
|
176
|
+
}, [n, e, s, f]);
|
|
177
|
+
const d = k(n, e);
|
|
139
178
|
return {
|
|
140
179
|
value: n,
|
|
141
180
|
displayValue: n,
|
|
142
|
-
rawValue:
|
|
143
|
-
ref:
|
|
181
|
+
rawValue: d,
|
|
182
|
+
ref: v
|
|
144
183
|
};
|
|
145
184
|
}
|
|
146
185
|
export {
|
|
147
|
-
|
|
186
|
+
j as useInputNumberMask
|
|
148
187
|
};
|
|
149
188
|
//# sourceMappingURL=index.mjs.map
|
package/dist/index.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.mjs","sources":["../src/utils/maskUtils.ts","../src/useInputNumberMask.ts"],"sourcesContent":["export const isDigit = (char: string) => /\\d/.test(char);\n\nexport const cleanInput = (input: string, template: string): string => {\n let extracted = '';\n let t = 0;\n\n for (let i = 0; i < input.length; i++) {\n const char = input[i];\n\n if (t >= template.length) {\n break;\n }\n\n if (template[t] === 'd') {\n if (isDigit(char)) {\n extracted += char;\n t++;\n } else {\n // Ignore placeholders or garbage\n }\n } else if (template[t] === char) {\n t++;\n } else {\n let nextT = t;\n while (nextT < template.length && template[nextT] !== 'd' && template[nextT] !== char) {\n nextT++;\n }\n if (nextT < template.length && template[nextT] === 'd') {\n if (isDigit(char)) {\n extracted += char;\n t = nextT + 1;\n }\n } else if (nextT < template.length && template[nextT] === char) {\n t = nextT + 1;\n }\n }\n }\n return extracted;\n};\n\nexport const formatWithMask = (digits: string, template: string, placeholder?: string): string => {\n let res = '';\n let dIdx = 0;\n\n for (let i = 0; i < template.length; i++) {\n const isSlot = template[i] === 'd';\n\n if (isSlot) {\n if (dIdx < digits.length) {\n res += digits[dIdx++];\n } else {\n // Empty slot\n if (placeholder && i < placeholder.length) {\n res += placeholder[i];\n } else {\n if (!placeholder) {\n break;\n }\n }\n }\n } else {\n res += template[i];\n }\n }\n return res;\n};\n","import { useRef, useState, useLayoutEffect } from 'react';\nimport { cleanInput, formatWithMask, isDigit } from './utils/maskUtils';\n\nexport interface UseInputNumberMaskProps {\n /**\n * The mask template. 'd' represents a digit slot.\n * All other characters are treated as literals.\n * @example \"+1 (ddd) ddd-dddd\" for US phone numbers\n */\n template: string;\n\n /**\n * Optional full-length placeholder shown in empty slots.\n * Should match the template length.\n * @example \"mm/dd/yyyy\" for a date mask\n */\n placeholder?: string;\n\n /**\n * If true, deletion replaces with placeholder char\n * instead of shifting subsequent digits left.\n * @default false\n */\n keepPosition?: boolean;\n}\n\nexport interface UseInputNumberMaskReturn {\n /** \n * The raw value with formatting applied \n * @example \"+1 (234) 567-8900\" \n */\n value: string;\n /** \n * Deprecated: alias for value. The value to display in the input. \n * @example \"+1 (234) 567-8900\" \n */\n displayValue: string;\n /** \n * The unmasked digits only \n * @example \"1234567890\" \n */\n rawValue: string;\n /** \n * Ref to be attached to the HTML input element \n */\n ref: React.RefObject<HTMLInputElement | null>;\n}\n\nexport function useInputNumberMask({\n template,\n placeholder,\n keepPosition = false,\n}: UseInputNumberMaskProps): UseInputNumberMaskReturn {\n\n const [value, setValue] = useState(() => formatWithMask('', template, placeholder));\n const [cursor, setCursor] = useState<number | null>(null);\n const ref = useRef<HTMLInputElement>(null);\n\n useLayoutEffect(() => {\n if (ref.current && cursor !== null) {\n ref.current.setSelectionRange(cursor, cursor);\n }\n }, [cursor, value]);\n\n // Ref-based Event Handling\n useLayoutEffect(() => {\n const input = ref.current;\n if (!input) return;\n\n const handleKeyDown = (e: globalThis.KeyboardEvent) => {\n const el = e.target as HTMLInputElement;\n const start = el.selectionStart || 0;\n const end = el.selectionEnd || 0;\n const isSelection = start !== end;\n\n if (e.key === 'Backspace') {\n if (start === 0 && !isSelection) return;\n // We prevent default to handle the state update manually\n e.preventDefault();\n\n let deleteIndex = start - 1;\n\n if (isSelection) {\n if (keepPosition) {\n // Replace range with placeholders\n let newVal = value;\n for (let i = start; i < end; i++) {\n if (template[i] === 'd') {\n const pChar = placeholder && i < placeholder.length ? placeholder[i] : '_';\n newVal = newVal.substring(0, i) + pChar + newVal.substring(i + 1);\n }\n }\n setValue(newVal);\n setCursor(start);\n return;\n } else {\n // Standard delete range (shift)\n const newValRaw = value.slice(0, start) + value.slice(end);\n const newDigits = cleanInput(newValRaw, template);\n const formatted = formatWithMask(newDigits, template, placeholder);\n setValue(formatted);\n setCursor(start);\n return;\n }\n }\n\n // Single char backspace\n while (deleteIndex >= 0) {\n const isTemplateLiteral = template[deleteIndex] !== 'd';\n if (isTemplateLiteral) {\n deleteIndex--;\n } else {\n break;\n }\n }\n\n if (deleteIndex < 0) return; // Nothing to delete\n\n if (keepPosition) {\n const pChar = placeholder && deleteIndex < placeholder.length ? placeholder[deleteIndex] : '_';\n const newVal = value.substring(0, deleteIndex) + pChar + value.substring(deleteIndex + 1);\n setValue(newVal);\n setCursor(deleteIndex);\n } else {\n // Shift behavior\n const newValRaw = value.slice(0, deleteIndex) + value.slice(deleteIndex + 1);\n const newDigits = cleanInput(newValRaw, template);\n const formatted = formatWithMask(newDigits, template, placeholder);\n setValue(formatted);\n setCursor(deleteIndex);\n }\n } else if (e.key === 'Delete') {\n e.preventDefault();\n // Delete forward\n if (isSelection) {\n if (keepPosition) {\n let newVal = value;\n for (let i = start; i < end; i++) {\n if (template[i] === 'd') {\n const pChar = placeholder && i < placeholder.length ? placeholder[i] : '_';\n newVal = newVal.substring(0, i) + pChar + newVal.substring(i + 1);\n }\n }\n setValue(newVal);\n setCursor(start);\n return;\n } else {\n const newValRaw = value.slice(0, start) + value.slice(end);\n const newDigits = cleanInput(newValRaw, template);\n const formatted = formatWithMask(newDigits, template, placeholder);\n setValue(formatted);\n setCursor(start);\n return;\n }\n }\n\n // Single char delete\n let deleteIndex = start;\n while (deleteIndex < template.length) {\n const isTemplateLiteral = template[deleteIndex] !== 'd';\n if (isTemplateLiteral) {\n deleteIndex++;\n } else {\n break;\n }\n }\n\n if (deleteIndex >= value.length) return;\n\n if (keepPosition) {\n const pChar = placeholder && deleteIndex < placeholder.length ? placeholder[deleteIndex] : '_';\n const newVal = value.substring(0, deleteIndex) + pChar + value.substring(deleteIndex + 1);\n setValue(newVal);\n setCursor(start);\n } else {\n // Shift behavior\n const newValRaw = value.slice(0, deleteIndex) + value.slice(deleteIndex + 1);\n const newDigits = cleanInput(newValRaw, template);\n const formatted = formatWithMask(newDigits, template, placeholder);\n setValue(formatted);\n setCursor(start);\n }\n }\n };\n\n const handleInput = (e: Event) => {\n // We cast to InputEvent or basic Event. target is HTMLInputElement.\n const target = e.target as HTMLInputElement;\n const inputVal = target.value;\n const rawCursor = target.selectionStart || 0;\n\n // Standard behavior logic\n const standardChange = () => {\n const beforeCursor = inputVal.slice(0, rawCursor);\n const digitsBeforeCursor = cleanInput(beforeCursor, template).length;\n\n const newDigits = cleanInput(inputVal, template);\n const newFormatted = formatWithMask(newDigits, template, placeholder);\n\n setValue(newFormatted);\n\n let currentDigits = 0;\n let newCursor = 0;\n for (let i = 0; i < newFormatted.length; i++) {\n if (currentDigits >= digitsBeforeCursor) {\n break;\n }\n if (isDigit(newFormatted[i]) && template[i] === 'd') {\n currentDigits++;\n }\n newCursor++;\n }\n\n while (newCursor < newFormatted.length && template[newCursor] !== 'd') {\n newCursor++;\n }\n\n setCursor(newCursor);\n };\n\n if (keepPosition) {\n // Measure diff against current state `value` (mapped in closure)\n // NOTE: `value` in this closure is stale if not included in dependency array.\n // We need strict dependency on `value`.\n if (inputVal.length > value.length) {\n const insertIndex = rawCursor - 1;\n const char = inputVal[insertIndex];\n\n if (!isDigit(char)) {\n standardChange();\n return;\n }\n\n let targetIndex = insertIndex;\n while (targetIndex < template.length) {\n if (template[targetIndex] === 'd') {\n break;\n }\n targetIndex++;\n }\n\n if (targetIndex >= template.length) {\n standardChange();\n return;\n }\n\n const newValue = value.substring(0, targetIndex) + char + value.substring(targetIndex + 1);\n\n setValue(newValue);\n setCursor(targetIndex + 1);\n return;\n }\n }\n\n standardChange();\n };\n\n input.addEventListener('keydown', handleKeyDown);\n input.addEventListener('input', handleInput);\n\n return () => {\n input.removeEventListener('keydown', handleKeyDown);\n input.removeEventListener('input', handleInput);\n };\n }, [value, template, placeholder, keepPosition]); // Re-bind when value changes to have fresh closure\n\n const rawValue = cleanInput(value, template);\n\n return {\n value,\n displayValue: value,\n rawValue,\n ref\n };\n}\n"],"names":["isDigit","char","cleanInput","input","template","extracted","t","i","nextT","formatWithMask","digits","placeholder","res","dIdx","useInputNumberMask","keepPosition","value","setValue","useState","cursor","setCursor","ref","useRef","useLayoutEffect","handleKeyDown","e","el","start","end","isSelection","deleteIndex","newVal","pChar","newValRaw","newDigits","formatted","handleInput","target","inputVal","rawCursor","standardChange","beforeCursor","digitsBeforeCursor","newFormatted","currentDigits","newCursor","insertIndex","targetIndex","newValue","rawValue"],"mappings":";AAAO,MAAMA,IAAU,CAACC,MAAiB,KAAK,KAAKA,CAAI,GAE1CC,IAAa,CAACC,GAAeC,MAA6B;AACnE,MAAIC,IAAY,IACZC,IAAI;AAER,WAASC,IAAI,GAAGA,IAAIJ,EAAM,QAAQI,KAAK;AACnC,UAAMN,IAAOE,EAAMI,CAAC;AAEpB,QAAID,KAAKF,EAAS;AACd;AAGJ,QAAIA,EAASE,CAAC,MAAM;AAChB,MAAIN,EAAQC,CAAI,MACZI,KAAaJ,GACbK;AAAA,aAIGF,EAASE,CAAC,MAAML;AACvB,MAAAK;AAAA,SACG;AACH,UAAIE,IAAQF;AACZ,aAAOE,IAAQJ,EAAS,UAAUA,EAASI,CAAK,MAAM,OAAOJ,EAASI,CAAK,MAAMP;AAC7E,QAAAO;AAEJ,MAAIA,IAAQJ,EAAS,UAAUA,EAASI,CAAK,MAAM,MAC3CR,EAAQC,CAAI,MACZI,KAAaJ,GACbK,IAAIE,IAAQ,KAETA,IAAQJ,EAAS,UAAUA,EAASI,CAAK,MAAMP,MACtDK,IAAIE,IAAQ;AAAA,IAEpB;AAAA,EACJ;AACA,SAAOH;AACX,GAEaI,IAAiB,CAACC,GAAgBN,GAAkBO,MAAiC;AAC9F,MAAIC,IAAM,IACNC,IAAO;AAEX,WAASN,IAAI,GAAGA,IAAIH,EAAS,QAAQG;AAGjC,QAFeH,EAASG,CAAC,MAAM;AAG3B,UAAIM,IAAOH,EAAO;AACd,QAAAE,KAAOF,EAAOG,GAAM;AAAA,eAGhBF,KAAeJ,IAAII,EAAY;AAC/B,QAAAC,KAAOD,EAAYJ,CAAC;AAAA,eAEhB,CAACI;AACD;AAAA;AAKZ,MAAAC,KAAOR,EAASG,CAAC;AAGzB,SAAOK;AACX;ACjBO,SAASE,EAAmB;AAAA,EAC/B,UAAAV;AAAA,EACA,aAAAO;AAAA,EACA,cAAAI,IAAe;AACnB,GAAsD;AAElD,QAAM,CAACC,GAAOC,CAAQ,IAAIC,EAAS,MAAMT,EAAe,IAAIL,GAAUO,CAAW,CAAC,GAC5E,CAACQ,GAAQC,CAAS,IAAIF,EAAwB,IAAI,GAClDG,IAAMC,EAAyB,IAAI;AAEzC,EAAAC,EAAgB,MAAM;AAClB,IAAIF,EAAI,WAAWF,MAAW,QAC1BE,EAAI,QAAQ,kBAAkBF,GAAQA,CAAM;AAAA,EAEpD,GAAG,CAACA,GAAQH,CAAK,CAAC,GAGlBO,EAAgB,MAAM;AAClB,UAAMpB,IAAQkB,EAAI;AAClB,QAAI,CAAClB,EAAO;AAEZ,UAAMqB,IAAgB,CAACC,MAAgC;AACnD,YAAMC,IAAKD,EAAE,QACPE,IAAQD,EAAG,kBAAkB,GAC7BE,IAAMF,EAAG,gBAAgB,GACzBG,IAAcF,MAAUC;AAE9B,UAAIH,EAAE,QAAQ,aAAa;AACvB,YAAIE,MAAU,KAAK,CAACE,EAAa;AAEjC,QAAAJ,EAAE,eAAA;AAEF,YAAIK,IAAcH,IAAQ;AAE1B,YAAIE;AACA,cAAId,GAAc;AAEd,gBAAIgB,IAASf;AACb,qBAAST,IAAIoB,GAAOpB,IAAIqB,GAAKrB;AACzB,kBAAIH,EAASG,CAAC,MAAM,KAAK;AACrB,sBAAMyB,IAAQrB,KAAeJ,IAAII,EAAY,SAASA,EAAYJ,CAAC,IAAI;AACvE,gBAAAwB,IAASA,EAAO,UAAU,GAAGxB,CAAC,IAAIyB,IAAQD,EAAO,UAAUxB,IAAI,CAAC;AAAA,cACpE;AAEJ,YAAAU,EAASc,CAAM,GACfX,EAAUO,CAAK;AACf;AAAA,UACJ,OAAO;AAEH,kBAAMM,IAAYjB,EAAM,MAAM,GAAGW,CAAK,IAAIX,EAAM,MAAMY,CAAG,GACnDM,IAAYhC,EAAW+B,GAAW7B,CAAQ,GAC1C+B,IAAY1B,EAAeyB,GAAW9B,GAAUO,CAAW;AACjE,YAAAM,EAASkB,CAAS,GAClBf,EAAUO,CAAK;AACf;AAAA,UACJ;AAIJ,eAAOG,KAAe,KACQ1B,EAAS0B,CAAW,MAAM;AAEhD,UAAAA;AAMR,YAAIA,IAAc,EAAG;AAErB,YAAIf,GAAc;AACd,gBAAMiB,IAAQrB,KAAemB,IAAcnB,EAAY,SAASA,EAAYmB,CAAW,IAAI,KACrFC,IAASf,EAAM,UAAU,GAAGc,CAAW,IAAIE,IAAQhB,EAAM,UAAUc,IAAc,CAAC;AACxF,UAAAb,EAASc,CAAM,GACfX,EAAUU,CAAW;AAAA,QACzB,OAAO;AAEH,gBAAMG,IAAYjB,EAAM,MAAM,GAAGc,CAAW,IAAId,EAAM,MAAMc,IAAc,CAAC,GACrEI,IAAYhC,EAAW+B,GAAW7B,CAAQ,GAC1C+B,IAAY1B,EAAeyB,GAAW9B,GAAUO,CAAW;AACjE,UAAAM,EAASkB,CAAS,GAClBf,EAAUU,CAAW;AAAA,QACzB;AAAA,MACJ,WAAWL,EAAE,QAAQ,UAAU;AAG3B,YAFAA,EAAE,eAAA,GAEEI;AACA,cAAId,GAAc;AACd,gBAAIgB,IAASf;AACb,qBAAST,IAAIoB,GAAOpB,IAAIqB,GAAKrB;AACzB,kBAAIH,EAASG,CAAC,MAAM,KAAK;AACrB,sBAAMyB,IAAQrB,KAAeJ,IAAII,EAAY,SAASA,EAAYJ,CAAC,IAAI;AACvE,gBAAAwB,IAASA,EAAO,UAAU,GAAGxB,CAAC,IAAIyB,IAAQD,EAAO,UAAUxB,IAAI,CAAC;AAAA,cACpE;AAEJ,YAAAU,EAASc,CAAM,GACfX,EAAUO,CAAK;AACf;AAAA,UACJ,OAAO;AACH,kBAAMM,IAAYjB,EAAM,MAAM,GAAGW,CAAK,IAAIX,EAAM,MAAMY,CAAG,GACnDM,IAAYhC,EAAW+B,GAAW7B,CAAQ,GAC1C+B,IAAY1B,EAAeyB,GAAW9B,GAAUO,CAAW;AACjE,YAAAM,EAASkB,CAAS,GAClBf,EAAUO,CAAK;AACf;AAAA,UACJ;AAIJ,YAAIG,IAAcH;AAClB,eAAOG,IAAc1B,EAAS,UACAA,EAAS0B,CAAW,MAAM;AAEhD,UAAAA;AAMR,YAAIA,KAAed,EAAM,OAAQ;AAEjC,YAAID,GAAc;AACd,gBAAMiB,IAAQrB,KAAemB,IAAcnB,EAAY,SAASA,EAAYmB,CAAW,IAAI,KACrFC,IAASf,EAAM,UAAU,GAAGc,CAAW,IAAIE,IAAQhB,EAAM,UAAUc,IAAc,CAAC;AACxF,UAAAb,EAASc,CAAM,GACfX,EAAUO,CAAK;AAAA,QACnB,OAAO;AAEH,gBAAMM,IAAYjB,EAAM,MAAM,GAAGc,CAAW,IAAId,EAAM,MAAMc,IAAc,CAAC,GACrEI,IAAYhC,EAAW+B,GAAW7B,CAAQ,GAC1C+B,IAAY1B,EAAeyB,GAAW9B,GAAUO,CAAW;AACjE,UAAAM,EAASkB,CAAS,GAClBf,EAAUO,CAAK;AAAA,QACnB;AAAA,MACJ;AAAA,IACJ,GAEMS,IAAc,CAACX,MAAa;AAE9B,YAAMY,IAASZ,EAAE,QACXa,IAAWD,EAAO,OAClBE,IAAYF,EAAO,kBAAkB,GAGrCG,IAAiB,MAAM;AACzB,cAAMC,IAAeH,EAAS,MAAM,GAAGC,CAAS,GAC1CG,IAAqBxC,EAAWuC,GAAcrC,CAAQ,EAAE,QAExD8B,IAAYhC,EAAWoC,GAAUlC,CAAQ,GACzCuC,IAAelC,EAAeyB,GAAW9B,GAAUO,CAAW;AAEpE,QAAAM,EAAS0B,CAAY;AAErB,YAAIC,IAAgB,GAChBC,IAAY;AAChB,iBAAStC,IAAI,GAAGA,IAAIoC,EAAa,UACzB,EAAAC,KAAiBF,IADgBnC;AAIrC,UAAIP,EAAQ2C,EAAapC,CAAC,CAAC,KAAKH,EAASG,CAAC,MAAM,OAC5CqC,KAEJC;AAGJ,eAAOA,IAAYF,EAAa,UAAUvC,EAASyC,CAAS,MAAM;AAC9D,UAAAA;AAGJ,QAAAzB,EAAUyB,CAAS;AAAA,MACvB;AAEA,UAAI9B,KAIIuB,EAAS,SAAStB,EAAM,QAAQ;AAChC,cAAM8B,IAAcP,IAAY,GAC1BtC,IAAOqC,EAASQ,CAAW;AAEjC,YAAI,CAAC9C,EAAQC,CAAI,GAAG;AAChB,UAAAuC,EAAA;AACA;AAAA,QACJ;AAEA,YAAIO,IAAcD;AAClB,eAAOC,IAAc3C,EAAS,UACtBA,EAAS2C,CAAW,MAAM;AAG9B,UAAAA;AAGJ,YAAIA,KAAe3C,EAAS,QAAQ;AAChC,UAAAoC,EAAA;AACA;AAAA,QACJ;AAEA,cAAMQ,IAAWhC,EAAM,UAAU,GAAG+B,CAAW,IAAI9C,IAAOe,EAAM,UAAU+B,IAAc,CAAC;AAEzF,QAAA9B,EAAS+B,CAAQ,GACjB5B,EAAU2B,IAAc,CAAC;AACzB;AAAA,MACJ;AAGJ,MAAAP,EAAA;AAAA,IACJ;AAEA,WAAArC,EAAM,iBAAiB,WAAWqB,CAAa,GAC/CrB,EAAM,iBAAiB,SAASiC,CAAW,GAEpC,MAAM;AACT,MAAAjC,EAAM,oBAAoB,WAAWqB,CAAa,GAClDrB,EAAM,oBAAoB,SAASiC,CAAW;AAAA,IAClD;AAAA,EACJ,GAAG,CAACpB,GAAOZ,GAAUO,GAAaI,CAAY,CAAC;AAE/C,QAAMkC,IAAW/C,EAAWc,GAAOZ,CAAQ;AAE3C,SAAO;AAAA,IACH,OAAAY;AAAA,IACA,cAAcA;AAAA,IACd,UAAAiC;AAAA,IACA,KAAA5B;AAAA,EAAA;AAER;"}
|
|
1
|
+
{"version":3,"file":"index.mjs","sources":["../src/utils/maskUtils.ts","../src/useInputNumberMask.ts"],"sourcesContent":["export const isDigit = (char: string) => /\\d/.test(char);\n\nexport const cleanInput = (input: string, template: string): string => {\n let extracted = '';\n let t = 0;\n\n for (let i = 0; i < input.length; i++) {\n const char = input[i];\n\n if (t >= template.length) {\n break;\n }\n\n if (template[t] === 'd') {\n if (isDigit(char)) {\n extracted += char;\n t++;\n } else {\n // Ignore placeholders or garbage\n }\n } else if (template[t] === char) {\n t++;\n } else {\n let nextT = t;\n while (nextT < template.length && template[nextT] !== 'd' && template[nextT] !== char) {\n nextT++;\n }\n if (nextT < template.length && template[nextT] === 'd') {\n if (isDigit(char)) {\n extracted += char;\n t = nextT + 1;\n }\n } else if (nextT < template.length && template[nextT] === char) {\n t = nextT + 1;\n }\n }\n }\n return extracted;\n};\n\nexport const formatWithMask = (digits: string, template: string, placeholder?: string): string => {\n let res = '';\n let dIdx = 0;\n\n for (let i = 0; i < template.length; i++) {\n const isSlot = template[i] === 'd';\n\n if (isSlot) {\n if (dIdx < digits.length) {\n res += digits[dIdx++];\n } else {\n // Empty slot\n if (placeholder && i < placeholder.length) {\n res += placeholder[i];\n } else {\n if (!placeholder) {\n break;\n }\n }\n }\n } else {\n res += template[i];\n }\n }\n return res;\n};\n\nexport const applyKeepPositionChange = (\n value: string,\n template: string,\n placeholder: string | undefined,\n start: number,\n end: number,\n char: string\n): { value: string; cursor: number } => {\n let newValue = value;\n let newCursor = start;\n\n // Deletion\n if (char === '') {\n for (let i = start; i < end; i++) {\n if (template[i] === 'd') {\n const pChar = placeholder && i < placeholder.length ? placeholder[i] : '_';\n newValue = newValue.substring(0, i) + pChar + newValue.substring(i + 1);\n }\n }\n return { value: newValue, cursor: start };\n }\n\n // Replacement (Selection)\n if (start !== end) {\n // 1. Replace the first digit slot at start (if it's a digit slot)\n // Actually, if we selected a range, we typically want to replace the *entire* range content\n // with the new char *at the beginning*, and clear the rest.\n // But we must respect the template chars.\n\n // Strategy:\n // - Clear the entire range first (replace with placeholders)\n // - Insert the char at the first available digit slot within the range (or shortly after if range starts on literal?)\n // Wait, standard input behavior replacing a selection \"12/34\" with \"5\":\n // \"12/5...\"\n // So effectively: delete range, then insert char.\n\n // Let's implement it as:\n // 1. Check if first char of selection matches the typed char\n // If so, skip it and insert at the next digit slot\n let insertStart = start;\n if (isDigit(char) && value[start] === char) {\n // The first position already has the same digit, so skip it\n insertStart = start + 1;\n // Skip any literals to find the next digit slot\n while (insertStart < end && template[insertStart] !== 'd') {\n insertStart++;\n }\n }\n\n // 2. Clear the range from insertStart to end (fill with placeholders)\n for (let i = insertStart; i < end; i++) {\n if (template[i] === 'd') {\n const pChar = placeholder && i < placeholder.length ? placeholder[i] : '_';\n newValue = newValue.substring(0, i) + pChar + newValue.substring(i + 1);\n }\n }\n\n // 3. Insert the char at insertStart (finding closest digit slot)\n let targetIndex = insertStart;\n while (targetIndex < template.length) {\n if (template[targetIndex] === 'd') {\n break;\n }\n targetIndex++;\n }\n\n if (targetIndex < template.length && isDigit(char)) {\n newValue = newValue.substring(0, targetIndex) + char + newValue.substring(targetIndex + 1);\n newCursor = targetIndex + 1;\n } else {\n newCursor = start;\n }\n\n return { value: newValue, cursor: newCursor };\n }\n\n // Insertion (No selection)\n // Find next digit slot\n let targetIndex = start;\n while (targetIndex < template.length) {\n if (template[targetIndex] === 'd') {\n break;\n }\n targetIndex++;\n }\n\n if (targetIndex < template.length && isDigit(char)) {\n newValue = newValue.substring(0, targetIndex) + char + newValue.substring(targetIndex + 1);\n newCursor = targetIndex + 1;\n } else {\n newCursor = start; // No change\n }\n\n return { value: newValue, cursor: newCursor };\n};\n","import { useRef, useState, useLayoutEffect } from 'react';\nimport { cleanInput, formatWithMask, isDigit, applyKeepPositionChange } from './utils/maskUtils';\n\nexport interface UseInputNumberMaskProps {\n /**\n * The mask template. 'd' represents a digit slot.\n * All other characters are treated as literals.\n * @example \"+1 (ddd) ddd-dddd\" for US phone numbers\n */\n template: string;\n\n /**\n * Optional full-length placeholder shown in empty slots.\n * Should match the template length.\n * @example \"mm/dd/yyyy\" for a date mask\n */\n placeholder?: string;\n\n /**\n * If true, deletion replaces with placeholder char\n * instead of shifting subsequent digits left.\n * @default false\n */\n keepPosition?: boolean;\n}\n\nexport interface UseInputNumberMaskReturn {\n /** \n * The raw value with formatting applied \n * @example \"+1 (234) 567-8900\" \n */\n value: string;\n /** \n * Deprecated: alias for value. The value to display in the input. \n * @example \"+1 (234) 567-8900\" \n */\n displayValue: string;\n /** \n * The unmasked digits only \n * @example \"1234567890\" \n */\n rawValue: string;\n /** \n * Ref to be attached to the HTML input element \n */\n ref: React.RefObject<HTMLInputElement | null>;\n}\n\nexport function useInputNumberMask({\n template,\n placeholder,\n keepPosition = false,\n}: UseInputNumberMaskProps): UseInputNumberMaskReturn {\n\n const [value, setValue] = useState(() => formatWithMask('', template, placeholder));\n const [cursor, setCursor] = useState<number | null>(null);\n const ref = useRef<HTMLInputElement>(null);\n const selectionRef = useRef<{ start: number; end: number }>({ start: 0, end: 0 });\n\n useLayoutEffect(() => {\n if (ref.current && cursor !== null) {\n ref.current.setSelectionRange(cursor, cursor);\n }\n }, [cursor, value]);\n\n // Ref-based Event Handling\n useLayoutEffect(() => {\n const input = ref.current;\n if (!input) return;\n\n const updateSelection = () => {\n if (input) {\n selectionRef.current = {\n start: input.selectionStart || 0,\n end: input.selectionEnd || 0,\n };\n }\n };\n\n const handleKeyDown = (e: globalThis.KeyboardEvent) => {\n updateSelection();\n const el = e.target as HTMLInputElement;\n const start = el.selectionStart || 0;\n const end = el.selectionEnd || 0;\n const isSelection = start !== end;\n\n if (e.key === 'Backspace') {\n if (start === 0 && !isSelection) return;\n // We prevent default to handle the state update manually\n e.preventDefault();\n\n let deleteIndex = start - 1;\n\n if (isSelection) {\n if (keepPosition) {\n // Replace range with placeholders\n let newVal = value;\n for (let i = start; i < end; i++) {\n if (template[i] === 'd') {\n const pChar = placeholder && i < placeholder.length ? placeholder[i] : '_';\n newVal = newVal.substring(0, i) + pChar + newVal.substring(i + 1);\n }\n }\n setValue(newVal);\n setCursor(start);\n return;\n } else {\n // Standard delete range (shift)\n const newValRaw = value.slice(0, start) + value.slice(end);\n const newDigits = cleanInput(newValRaw, template);\n const formatted = formatWithMask(newDigits, template, placeholder);\n setValue(formatted);\n setCursor(start);\n return;\n }\n }\n\n // Single char backspace\n while (deleteIndex >= 0) {\n const isTemplateLiteral = template[deleteIndex] !== 'd';\n if (isTemplateLiteral) {\n deleteIndex--;\n } else {\n break;\n }\n }\n\n if (deleteIndex < 0) return; // Nothing to delete\n\n if (keepPosition) {\n const pChar = placeholder && deleteIndex < placeholder.length ? placeholder[deleteIndex] : '_';\n const newVal = value.substring(0, deleteIndex) + pChar + value.substring(deleteIndex + 1);\n setValue(newVal);\n setCursor(deleteIndex);\n } else {\n // Shift behavior\n const newValRaw = value.slice(0, deleteIndex) + value.slice(deleteIndex + 1);\n const newDigits = cleanInput(newValRaw, template);\n const formatted = formatWithMask(newDigits, template, placeholder);\n setValue(formatted);\n setCursor(deleteIndex);\n }\n } else if (e.key === 'Delete') {\n e.preventDefault();\n // Delete forward\n if (isSelection) {\n if (keepPosition) {\n let newVal = value;\n for (let i = start; i < end; i++) {\n if (template[i] === 'd') {\n const pChar = placeholder && i < placeholder.length ? placeholder[i] : '_';\n newVal = newVal.substring(0, i) + pChar + newVal.substring(i + 1);\n }\n }\n setValue(newVal);\n setCursor(start);\n return;\n } else {\n const newValRaw = value.slice(0, start) + value.slice(end);\n const newDigits = cleanInput(newValRaw, template);\n const formatted = formatWithMask(newDigits, template, placeholder);\n setValue(formatted);\n setCursor(start);\n return;\n }\n }\n\n // Single char delete\n let deleteIndex = start;\n while (deleteIndex < template.length) {\n const isTemplateLiteral = template[deleteIndex] !== 'd';\n if (isTemplateLiteral) {\n deleteIndex++;\n } else {\n break;\n }\n }\n\n if (deleteIndex >= value.length) return;\n\n if (keepPosition) {\n const pChar = placeholder && deleteIndex < placeholder.length ? placeholder[deleteIndex] : '_';\n const newVal = value.substring(0, deleteIndex) + pChar + value.substring(deleteIndex + 1);\n setValue(newVal);\n setCursor(start);\n } else {\n // Shift behavior\n const newValRaw = value.slice(0, deleteIndex) + value.slice(deleteIndex + 1);\n const newDigits = cleanInput(newValRaw, template);\n const formatted = formatWithMask(newDigits, template, placeholder);\n setValue(formatted);\n setCursor(start);\n }\n }\n };\n\n const handleInput = (e: Event) => {\n // We cast to InputEvent or basic Event. target is HTMLInputElement.\n const target = e.target as HTMLInputElement;\n const inputVal = target.value;\n const rawCursor = target.selectionStart || 0;\n\n // Standard behavior logic\n const standardChange = () => {\n const beforeCursor = inputVal.slice(0, rawCursor);\n const digitsBeforeCursor = cleanInput(beforeCursor, template).length;\n\n const newDigits = cleanInput(inputVal, template);\n const newFormatted = formatWithMask(newDigits, template, placeholder);\n\n setValue(newFormatted);\n\n let currentDigits = 0;\n let newCursor = 0;\n for (let i = 0; i < newFormatted.length; i++) {\n if (currentDigits >= digitsBeforeCursor) {\n break;\n }\n if (isDigit(newFormatted[i]) && template[i] === 'd') {\n currentDigits++;\n }\n newCursor++;\n }\n\n while (newCursor < newFormatted.length && template[newCursor] !== 'd') {\n newCursor++;\n }\n\n setCursor(newCursor);\n };\n\n if (keepPosition) {\n const prevStart = selectionRef.current.start;\n const prevEnd = selectionRef.current.end;\n const wasSelection = prevStart !== prevEnd;\n const selectionLength = prevEnd - prevStart;\n const typedChars = inputVal.length - value.length + selectionLength;\n\n // Handle single character input (typing one digit)\n if (typedChars === 1 && rawCursor > 0) {\n const insertIndex = rawCursor - 1;\n const char = inputVal[insertIndex];\n\n if (isDigit(char)) {\n // For selection replacement, use the selection range\n // For cursor insertion (no selection), use the cursor position\n const startPos = wasSelection ? prevStart : insertIndex;\n const endPos = wasSelection ? prevEnd : insertIndex;\n\n // Only apply keepPosition if within template bounds\n // If cursor is at/beyond template length (e.g., typing at end),\n // fall through to standardChange() for normal fill behavior\n if (startPos < template.length) {\n const result = applyKeepPositionChange(\n value,\n template,\n placeholder,\n startPos,\n endPos,\n char\n );\n\n setValue(result.value);\n setCursor(result.cursor);\n return;\n }\n }\n }\n }\n\n standardChange();\n };\n\n input.addEventListener('keydown', handleKeyDown);\n input.addEventListener('input', handleInput);\n input.addEventListener('select', updateSelection);\n input.addEventListener('click', updateSelection);\n input.addEventListener('keyup', updateSelection);\n\n return () => {\n input.removeEventListener('keydown', handleKeyDown);\n input.removeEventListener('input', handleInput);\n input.removeEventListener('select', updateSelection);\n input.removeEventListener('click', updateSelection);\n input.removeEventListener('keyup', updateSelection);\n };\n }, [value, template, placeholder, keepPosition]); // Re-bind when value changes to have fresh closure\n\n const rawValue = cleanInput(value, template);\n\n return {\n value,\n displayValue: value,\n rawValue,\n ref\n };\n}\n"],"names":["isDigit","char","cleanInput","input","template","extracted","t","i","nextT","formatWithMask","digits","placeholder","res","dIdx","applyKeepPositionChange","value","start","end","newValue","newCursor","pChar","insertStart","targetIndex","useInputNumberMask","keepPosition","setValue","useState","cursor","setCursor","ref","useRef","selectionRef","useLayoutEffect","updateSelection","handleKeyDown","e","el","isSelection","deleteIndex","newVal","newValRaw","newDigits","formatted","handleInput","target","inputVal","rawCursor","standardChange","beforeCursor","digitsBeforeCursor","newFormatted","currentDigits","prevStart","prevEnd","wasSelection","selectionLength","insertIndex","startPos","endPos","result","rawValue"],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tounsoo/input-number-mask",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.9",
|
|
4
4
|
"description": "A lightweight, dependency-free React hook for masking input values. Perfect for phone numbers, dates, credit cards, and more.",
|
|
5
|
-
"packageManager": "pnpm@10.23.0",
|
|
6
5
|
"type": "module",
|
|
7
6
|
"main": "./dist/index.cjs",
|
|
8
7
|
"module": "./dist/index.mjs",
|
|
@@ -24,21 +23,6 @@
|
|
|
24
23
|
"README.md",
|
|
25
24
|
"LICENSE"
|
|
26
25
|
],
|
|
27
|
-
"scripts": {
|
|
28
|
-
"dev": "vite",
|
|
29
|
-
"build": "tsc -b && vite build",
|
|
30
|
-
"build:lib": "tsc -p tsconfig.lib.json && vite build --config vite.config.lib.ts",
|
|
31
|
-
"lint": "eslint .",
|
|
32
|
-
"preview": "vite preview",
|
|
33
|
-
"storybook": "storybook dev -p 6006",
|
|
34
|
-
"build-storybook": "storybook build",
|
|
35
|
-
"test": "vitest run",
|
|
36
|
-
"test:ci": "vitest run --config vitest.config.ci.ts",
|
|
37
|
-
"prepublishOnly": "pnpm build:lib",
|
|
38
|
-
"changeset": "changeset",
|
|
39
|
-
"version": "changeset version",
|
|
40
|
-
"release": "pnpm build:lib && npm publish --access public --provenance && git push --follow-tags origin main"
|
|
41
|
-
},
|
|
42
26
|
"peerDependencies": {
|
|
43
27
|
"react": "^18.0.0 || ^19.0.0",
|
|
44
28
|
"react-dom": "^18.0.0 || ^19.0.0"
|
|
@@ -101,5 +85,19 @@
|
|
|
101
85
|
"publishConfig": {
|
|
102
86
|
"access": "public",
|
|
103
87
|
"provenance": true
|
|
88
|
+
},
|
|
89
|
+
"scripts": {
|
|
90
|
+
"dev": "vite",
|
|
91
|
+
"build": "tsc -b && vite build",
|
|
92
|
+
"build:lib": "tsc -p tsconfig.lib.json && vite build --config vite.config.lib.ts",
|
|
93
|
+
"lint": "eslint .",
|
|
94
|
+
"preview": "vite preview",
|
|
95
|
+
"storybook": "storybook dev -p 6006",
|
|
96
|
+
"build-storybook": "storybook build",
|
|
97
|
+
"test": "vitest run",
|
|
98
|
+
"test:ci": "vitest run --config vitest.config.ci.ts",
|
|
99
|
+
"changeset": "changeset",
|
|
100
|
+
"version": "changeset version",
|
|
101
|
+
"release": "pnpm build:lib && changeset publish"
|
|
104
102
|
}
|
|
105
103
|
}
|