reactjs-virtual-keyboard
Version:
A customizable virtual keyboard component for React applications with support for multiple layouts, multi-language support, hardware keyboard sync, and touch devices
2 lines (1 loc) • 12.6 kB
JavaScript
"use strict";const e=require("react/jsx-runtime"),t=require("react"),n=(e,t,n={})=>{var o;const{skipValueAssignment:s=!1}=n;if(!s){const n=Object.getPrototypeOf(e),s=null==(o=Object.getOwnPropertyDescriptor(n,"value"))?void 0:o.set;s&&s.call(e,t)}e.dispatchEvent(new InputEvent("input",{bubbles:!0,cancelable:!0})),e.dispatchEvent(new Event("change",{bubbles:!0,cancelable:!0}))},o=(e,t)=>{switch(t){case"number":return/^[0-9]*$/.test(e);case"email":return/^[a-zA-Z0-9@._+-]*$/.test(e);case"tel":return/^[0-9+\-() ]*$/.test(e);case"url":return/^[a-zA-Z0-9:/._-]*$/.test(e);default:return!0}},s=(e,t)=>"number"===e?"numbers":t,a="vk-keyboard-shift-transition";let r=[],l=!1;function c(e,t){r.length>0&&u();const n=function(e){const t=e.getBoundingClientRect().bottom+20-.62*window.innerHeight;return t>0?t:0}(e);if(0===n)return;const o=function(e){var t;return e?Array.from((null==(t=e.parentElement)?void 0:t.children)??[]).filter(t=>{if(t===e)return!1;const{position:n}=window.getComputedStyle(t);return"fixed"!==n&&"absolute"!==n}):[]}(t);if(0!==o.length){!function(){if(l)return;const e=document.createElement("style");e.id="vk-keyboard-shift-styles",e.textContent=`\n .${a} {\n transition: transform 300ms ease-out;\n will-change: transform;\n }\n `,document.head.appendChild(e),l=!0}(),r=o;for(const e of o)e.classList.add(a),e.style.transform=`translateY(-${n}px)`}}function u(){if(0===r.length)return;for(const t of r)document.body.contains(t)&&(t.style.transform="translateY(0)");const e=[...r];setTimeout(()=>{for(const t of e)document.body.contains(t)&&t.classList.remove(a)},300),r=[]}const i=e=>{var t;if(e.current){const n=e.current;n.blur(),null==(t=n.form)||t.submit()}},p=e=>{const t=e.target;if("TEXTAREA"===t.tagName)return t;if("INPUT"===t.tagName){const e=t;return["checkbox","radio","range","date","time","color","month","week","file","hidden","submit","reset","button","image"].includes(e.type)?null:e}return null};function d(e){return{insertText:t=>{const o=e();if(!o)return;if(o.readOnly||o.disabled)return;const s=o.selectionStart,a=o.selectionEnd,r=null==s?o.value.length:s,l=null==a?o.value.length:a,c=o.scrollTop??0,u=o.scrollLeft??0,i=o.value.slice(0,r)+t+o.value.slice(l),p=r+t.length;n(o,i),o.focus(),o.setSelectionRange(p,p),"scrollTop"in o&&(o.scrollTop=c,o.scrollLeft=u)},backspace:()=>{const t=e();if(!t)return;if(t.readOnly||t.disabled)return;const o=t.selectionStart,s=t.selectionEnd,a=null==o?t.value.length:o,r=null==s?t.value.length:s,l=t.scrollTop??0,c=t.scrollLeft??0;if(a!==r){const e=t.value.slice(0,a)+t.value.slice(r);n(t,e),t.focus(),t.setSelectionRange(a,a)}else if(a>0){const e=t.value.slice(0,a-1)+t.value.slice(a),o=a-1;n(t,e),t.focus(),t.setSelectionRange(o,o)}"scrollTop"in t&&(t.scrollTop=l,t.scrollLeft=c)}}}function v(e){const{onBackspace:t,onEnter:n,onSpace:o,onCapsToggle:s,onKeyClick:a}=e,r=e=>{const r=e.key;switch(r){case"Backspace":return e.preventDefault(),e.stopPropagation(),void t();case"Enter":return e.preventDefault(),void n();case" ":return e.preventDefault(),void o();case"CapsLock":return e.preventDefault(),void s();default:1===r.length&&(e.preventDefault(),a(r))}};return document.addEventListener("keydown",r),()=>{document.removeEventListener("keydown",r)}}const y=[["1","2","3","4","5","6","7","8","9","0"],["q","w","e","r","t","y","u","i","o","p"],["a","s","d","f","g","h","j","k","l"],["z","x","c","v","b","n","m"]],k=[["1","2","3","4","5","6","7","8","9","0"],["!","@","#","$","%","^","&","*","(",")"],["-","_","=","+","[","]","{","}","\\","|"],[";",":",'"',"'",",",".","<",">","/","?"]],x=[["7","8","9","#"],["4","5","6","-"],["1","2","3"],[",","0","."]],m=t=>e.jsx("svg",{xmlns:"http://www.w3.org/2000/svg",viewBox:"0 0 24 24",fill:"currentColor",width:"1em",height:"1em",...t,children:e.jsx("path",{d:"M22 3H7c-.69 0-1.23.35-1.59.88L0 12l5.41 8.11c.36.53.9.89 1.59.89h15c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2zm0 16H7.07L2.4 12l4.66-7H22v14zm-11.59-2L14 13.41 17.59 17 19 15.59 15.41 12 19 8.41 17.59 7 14 10.59 10.41 7 9 8.41 12.59 12 9 15.59z"})}),f=t=>e.jsx("svg",{xmlns:"http://www.w3.org/2000/svg",viewBox:"0 0 24 24",fill:"currentColor",width:"1em",height:"1em",...t,children:e.jsx("path",{d:"M19 7v4H5.83l3.58-3.59L8 6l-6 6 6 6 1.41-1.41L5.83 13H21V7z"})}),g=t=>e.jsx("svg",{xmlns:"http://www.w3.org/2000/svg",viewBox:"0 0 24 24",fill:"currentColor",width:"1em",height:"1em",...t,children:e.jsx("path",{d:"M18 9v4H6V9H4v6h16V9z"})}),C=t=>e.jsx("svg",{xmlns:"http://www.w3.org/2000/svg",viewBox:"0 0 24 24",fill:"currentColor",width:"1em",height:"1em",style:{transform:"rotate(270deg)"},...t,children:e.jsx("path",{d:"M12 4l-1.41 1.41L16.17 11H4v2h12.17l-5.58 5.59L12 20l8-8z"})});function h(e,{initialDelay:n=500,interval:o=50,shouldPreventDefault:s=!0}={}){const a=t.useRef(),r=t.useRef(),l=t.useCallback(()=>{a.current&&(clearTimeout(a.current),a.current=void 0),r.current&&(clearInterval(r.current),r.current=void 0)},[]),c=t.useCallback(()=>{e(),a.current=setTimeout(()=>{r.current=setInterval(e,o)},n)},[e,n,o]);return{onMouseDown:e=>{e.preventDefault(),c()},onTouchStart:e=>{s&&e.preventDefault(),c()},onMouseUp:l,onMouseLeave:l,onTouchEnd:e=>{s&&e.preventDefault(),l()}}}function b(e){const n=t.useCallback(t=>{setTimeout(()=>{(null==e?void 0:e.current)&&c(t,e.current)},0)},[e]),o=t.useCallback(()=>{u()},[]);return t.useEffect(()=>()=>{u()},[]),{scrollInput:n,resetScroll:o}}const L=t.memo(t=>{const{type:n,icon:o,onClick:s,extraClass:a="",text:r,capsLock:l=!1,enableContinuousPress:c=!1}=t,u="caps"===n&&l,i=["vk-key",`vk-key--${a}`,u?"vk-key--caps-active":""].filter(Boolean).join(" "),p=h(s,{initialDelay:500,interval:50});return e.jsxs("button",{type:"button",className:i,"data-testid":`${n}${u?"-active":""}`,"data-key":u?`${n}-active`:n,...c?p:{onClick:s},children:[o&&o,r&&e.jsx("span",{className:"vk-key__text",children:r})]})});L.displayName="SpecialKey";const j=t.memo(({keyValue:t,onClick:n,className:o=""})=>{const s=["vk-key",o].filter(Boolean).join(" ");return e.jsx("button",{type:"button",className:s,onClick:()=>n(t),"data-testid":t,children:t})});j.displayName="VirtualKey";const w=({children:t,className:n=""})=>{const o=["vk-row",n].filter(Boolean).join(" ");return e.jsx("div",{className:o,children:t})},E=({currentLayoutData:t,onBackspace:n,onEnter:o,onKeyClick:s,capsLock:a})=>e.jsx("div",{className:"vk-layout vk-layout--numbers","data-testid":"keyboard-layout",children:null==t?void 0:t.map((t,r)=>e.jsxs(w,{children:[null==t?void 0:t.map((t,n)=>e.jsx(j,{keyValue:t,onClick:s},`num-${r}-${n}-${t}`)),3===r&&e.jsx(L,{type:"enter",icon:e.jsx(f,{}),onClick:o,extraClass:"enter-num",text:"Enter",capsLock:a},"enter-num"),2===r&&e.jsx(L,{type:"backspace",icon:e.jsx(m,{}),onClick:n,extraClass:"backspace-num",text:"Backspace",capsLock:a,enableContinuousPress:!0},"backspace-num")]},`num-row-${r}`))}),T=({inputType:t,currentLayoutData:n,onBackspace:o,onEnter:s,onSpace:a,onCapsToggle:r,onLayoutToggle:l,onKeyClick:c,capsLock:u,currentLayout:i})=>{const p=t=>3===t?"letters"===i&&e.jsx(L,{type:"caps",icon:e.jsx(C,{}),onClick:r,extraClass:"capsLock",text:"Caps Lock",capsLock:u},"caps"):null,d=t=>3===t?e.jsx(L,{type:"backspace",icon:e.jsx(m,{}),onClick:o,extraClass:"backspace",text:"Backspace",enableContinuousPress:!0},"backspace"):null;return e.jsxs("div",{className:"vk-layout vk-layout--text","data-testid":"keyboard-layout",children:[null==n?void 0:n.map((t,n)=>e.jsxs(w,{children:[p(n),t.map((t,o)=>{const s=u?t.toUpperCase():t.toLowerCase();return e.jsx(j,{keyValue:s,onClick:c},`${n}-${o}-${t}`)}),d(n)]},`row-${n}`)),e.jsxs(w,{children:[e.jsx(L,{type:"layout",icon:"letters"===i?"&123":"ABC",onClick:l,extraClass:"layout",text:""},"layout"),"email"===t&&e.jsx(L,{type:"dot",onClick:()=>c("."),extraClass:"dot",icon:"."},"dot"),e.jsx(L,{type:"space",icon:e.jsx(g,{}),onClick:a,extraClass:"space",text:"Space"},"space"),"email"===t&&e.jsx(L,{type:"at",icon:"@",onClick:()=>c("@"),extraClass:"at",text:""},"at"),e.jsx(L,{type:"enter",icon:e.jsx(f,{}),onClick:s,extraClass:"enter",text:"Enter"},"enter")]})]})},S=({currentLayout:t,capsLock:n,onKeyClick:o,onBackspace:s,onEnter:a,onSpace:r,onCapsToggle:l,onLayoutToggle:c,inputType:u,customLayouts:i})=>{const p="letters"===t?(null==i?void 0:i.letters)||y:"symbols"===t?(null==i?void 0:i.symbols)||k:(null==i?void 0:i.numbers)||x;return"numbers"===t?e.jsx(E,{currentLayoutData:p,onBackspace:s,onEnter:a,onKeyClick:o,capsLock:n,currentLayout:t}):e.jsx(T,{inputType:u,currentLayoutData:p,onBackspace:s,onEnter:a,onSpace:r,onCapsToggle:l,onLayoutToggle:c,onKeyClick:o,capsLock:n,currentLayout:t})},B=({children:t,className:n=""})=>{const o=["vk-container",n].filter(Boolean).join(" ");return e.jsx("div",{className:o,onMouseDown:e=>{e.preventDefault()},onClick:e=>{e.preventDefault(),e.stopPropagation()},"data-testid":"keyboard-container",children:t})},N=({focusedInputRef:n,isInputFocused:a,inputType:r="text",onEnterClick:l,onChange:c,className:u,defaultLayout:i="letters",validate:p,syncWithHardwareKeyboard:y=!0,customLayouts:k,languages:x,currentLanguage:m,onLanguageChange:f,showLanguageSwitcher:g=!1})=>{const[C,h]=t.useState(!1),[b,L]=t.useState(m||Object.keys(x||{})[0]||"en"),{insertText:j,backspace:w}=d(()=>n.current),[E,T]=t.useState(()=>s(r,i)),N=t.useCallback(e=>{var t;p&&!p(e)||o(e,r)&&(j(e),null==c||c((null==(t=n.current)?void 0:t.value)??""))},[n,r,j,c,p]),D=t.useCallback(e=>{N(e)},[N]),K=t.useCallback(()=>{var e,t;0!==(null==(e=n.current)?void 0:e.value.length)&&(w(),null==c||c((null==(t=n.current)?void 0:t.value)??""))},[w,n,c]),I=t.useCallback(()=>{null==l||l()},[l]),R=t.useCallback(()=>{var e;N(" "),j(" "),null==c||c((null==(e=n.current)?void 0:e.value)||"")},[j,c,n]),$=t.useCallback(()=>{h(e=>!e)},[]);t.useEffect(()=>{T(s(r,i))},[r,i]);const V={onBackspace:K,onEnter:I,onSpace:R,onCapsToggle:$,onKeyClick:D};t.useEffect(()=>{if(a&&y)return v(V)},[a,y,D,K,I,R,$]);const A=(null==x?void 0:x[b])||k;return e.jsxs(B,{className:u,children:[g&&x&&Object.keys(x).length>1&&e.jsx("div",{className:"vk-language-switcher",children:Object.entries(x).map(([t,n])=>e.jsx("button",{className:"vk-lang-btn "+(b===t?"active":""),onClick:()=>{return L(e=t),void(null==f||f(e));var e},children:n.label||t.toUpperCase()},t))}),e.jsx(S,{capsLock:C,currentLayout:E,onKeyClick:D,onBackspace:K,onEnter:I,onSpace:R,onCapsToggle:$,onLayoutToggle:()=>T(e=>"letters"===e?"symbols":"letters"),inputType:r,customLayouts:A})]})};exports.BackspaceIcon=m,exports.CapsLockIcon=C,exports.DEFAULT_THEME={backgroundColor:"#1a1a1a",keyColor:"#444444",keyTextColor:"#ffffff",keyActiveColor:"#666666",keyHoverColor:"#555555",activeStateColor:"#4a90e2",keyBorderRadius:"0.5vw",keyFontSize:"32px",keyHeight:"100%"},exports.EnterIcon=f,exports.GlobalVirtualKeyboard=({enabled:n=!0,className:o,onVisibilityChange:s,onEnterClick:a,onChange:r})=>{const l=t.useRef(null),[c,u]=t.useState(!1),[d,v]=t.useState("text"),y=t.useRef(null),k=t.useRef("text"),{scrollInput:x,resetScroll:m}=b(l);return t.useEffect(()=>{if(!n)return void(c&&(setTimeout(()=>{u(!1)},0),null==s||s(!1)));const e=e=>{const t=p(e);if(!t)return;const n="TEXTAREA"===t.tagName;if(v(n?"text":t.type),u(!0),null==s||s(!0),y.current=t,!n&&(k.current=t.type,"text"!==t.type)){const e=t.selectionStart,n=t.selectionEnd;t.type="text";const o=e??t.value.length;t.setSelectionRange(o,n??o)}x(t)},t=e=>{const t=p(e);t&&("TEXTAREA"===t.tagName||(t.type=k.current),y.current===t&&(y.current=null,u(!1),null==s||s(!1),m()))};return document.addEventListener("focusin",e,!0),document.addEventListener("focusout",t,!0),()=>{document.removeEventListener("focusin",e,!0),document.removeEventListener("focusout",t,!0)}},[n,x,m,s]),c&&n?e.jsx("span",{ref:l,children:e.jsx(N,{focusedInputRef:y,isInputFocused:c,inputType:d,onEnterClick:()=>{u(!1),null==s||s(!1),i(y),null==a||a(),m()},onChange:r,className:o})}):null},exports.KeyboardLayout=S,exports.KeyboardRow=w,exports.NUMBERS_LAYOUT=x,exports.NumbersLayout=E,exports.QWERTY_LAYOUT=y,exports.SYMBOLS_LAYOUT=k,exports.SpacebarIcon=g,exports.SpecialKey=L,exports.TextLayout=T,exports.VirtualKey=j,exports.VirtualKeyboard=N,exports.VirtualKeyboardContainer=B,exports.createCaretManager=d,exports.getInitialLayout=s,exports.handleValueChangeUtil=(e,t)=>{const o=e.current;o&&n(o,t)},exports.onEnterClickUtil=i,exports.resetScrollPosition=u,exports.scrollInputIntoView=c,exports.setInputValueAndDispatchEvents=n,exports.setupHardwareKeyboard=v,exports.useContinuousPress=h,exports.useKeyboardScroll=b,exports.validateFocusInputs=p,exports.validateValueUtil=o;//# sourceMappingURL=index.js.map