UNPKG

react-input-suggestions

Version:

A React input component with pluggable suggestions and autocomplete

60 lines (50 loc) 3.63 kB
import{useEffect as S,useState as k}from"react";var E="ArrowDown",R="ArrowUp",C="Enter",K="Tab";var T=(t,n,o)=>{let[r,a]=k(!1);S(()=>{a(Boolean(t&&t.current&&t.current.value.length>0&&o.length>0))},[t,o]),S(()=>{let e=s=>{r&&!n.current?.contains(s.target)&&a(!1)};return n.current?.querySelectorAll("li")?.forEach(s=>{s.firstChild.tabIndex=0}),document.addEventListener("mousedown",e),()=>{document.removeEventListener("mousedown",e)}},[n,r]);let u=e=>{n.current?.querySelector(`li:${e}-of-type`)?.firstChild?.focus()},d=()=>n.current?.querySelector("li > *:focus"),g=e=>{e.currentTarget.value&&!d()&&[E,R].includes(e.key)&&(e.preventDefault(),e.key===E&&u("first"),e.key===R&&u("last"))},p=e=>{e?.currentTarget?.firstChild?.focus()},y=(e,s)=>{e.preventDefault();let m=e.currentTarget?.[`${s}`]?.firstChild;m?m.focus():u(s==="nextSibling"?"first":"last")},c=(e,s)=>{y(e,s)};return{selectInitialResult:g,onResultsHover:p,onResultsKeyDown:e=>{[E,K].includes(e.key)?c(e,"nextSibling"):e.key===R?c(e,"previousSibling"):e.key!==C&&t.current?.focus()},showSuggestions:r,setShowSuggestions:a,onInputFocus:e=>{document.activeElement===t.current&&e.currentTarget.value!==""&&a(!0)}}};import{useRef as w,useState as U}from"react";import N,{Children as A,cloneElement as D}from"react";import F from"react-string-replace";import{jsx as O}from"react/jsx-runtime";var l={get:t=>{if(["string","number"].includes(typeof t))return t;if(t instanceof Array)return[...new Set(t.map(l.get))].join(" ");if(typeof t=="object"&&t)return l.get(t.props.children)},highlightKeyword:(t,n)=>F(t,n,(o,r)=>O("mark",{children:o},r)),cloneChildren:(t,n)=>A.map(t,o=>o.props?D(o,{children:l.highlightKeyword(l.cloneChildren(o.props.children,n),n)}):o),wrap:(t,n)=>{let o=t,{props:{children:r}}=o;return N.cloneElement(o,{children:typeof r=="string"?l.highlightKeyword(r,n):l.cloneChildren(r,n)})}};import P from"@emotion/styled";var v=P.div` position: relative; input { width: 100%; } mark { display: inline; padding: 0; } ul { position: absolute; top: 100%; width: 100%; box-sizing: border-box; list-style-type: none; overflow-y: auto; li > * { display: block; cursor: pointer; text-decoration: none; &:focus { border: 0; box-shadow: 0; outline: 0; } } } ${({withTheme:t})=>t&&`input { font-size: 1rem; padding: 20px; border: 1px solid #dadada; background: #efefef; width: 100%; outline: 0; } ul { padding: 0; margin-top: -3px; border: 1px solid #dadada; box-shadow: 0 3px 3px rgba(0, 0, 0, 0.2); font-size: 1rem; li > * { padding: 1rem; &:focus { background: #efefef; } } } `} `;import{jsx as h,jsxs as X}from"react/jsx-runtime";var q=({suggestions:t,type:n="search",name:o="q",placeholder:r="Search",autoFocus:a=!1,className:u="",withTheme:d=!1,id:g,onChange:p,highlightKeywords:y=!1})=>{let[c,b]=U(t),f=w(null),e=w(null),{selectInitialResult:s,onResultsHover:m,onResultsKeyDown:L,showSuggestions:I,onInputFocus:x}=T(f,e,c),M=i=>b(t.filter(H=>l.get(H)?.toLowerCase().includes(i.target.value.toLowerCase()||"")));return X(v,{id:g,className:u,withTheme:d,children:[h("input",{ref:f,type:n,name:o,placeholder:r,autoFocus:a,onChange:i=>{p&&p(i),M(i)},onKeyDown:s,onFocus:x,spellCheck:!1,autoComplete:"off",autoCapitalize:"off"}),I&&h("ul",{ref:e,children:c.map(i=>h("li",{onMouseOver:m,onKeyDown:L,children:y?l.wrap(i,f.current?.value||""):i},l.get(i)))})]})},z=q;export{z as InputSuggestions,T as useSuggestions};