{"mappings":"AGAA,uBACI,IAAI,GACJ,IAAI,GACJ,IAAI,GACJ,IAAI,GACJ,IAAI,GACJ,UAAU,GACV,IAAI,GACJ,SAAS,GACT,IAAI,GACJ,IAAI,GACJ,IAAI,GACJ,IAAI,GACJ,IAAI,GACJ,IAAI,GACJ,IAAI,GACJ,IAAI,GACJ,IAAI,GACJ,IAAI,GACJ,IAAI,GACJ,IAAI,GACJ,IAAI,GACJ,IAAI,GACJ,IAAI,GACJ,IAAI,GACJ,IAAI,GACJ,IAAI,GACJ,IAAI,GACJ,IAAI,GACJ,IAAI,GACJ,IAAI,GACJ,IAAI,GACJ,IAAI,GACJ,IAAI,GACJ,IAAI,GACJ,IAAI,CAAC;ACjCT,wCACE,SAAQ,KAAK,CAAC,SAAS,CAAC,gBAAgB,GAAG,mBAAmB,CAAC;IAC/D;;;;OAIG;IACH,eAAe,CAAC,EAAE,CAAC,KAAK,EAAE,GAAG,KAAK,MAAM,SAAS,CAAC;IAElD;;;OAGG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC;IAEjB;;;OAGG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC;IAEjB;;OAEG;IACH,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAE5B;;OAEG;IACH,eAAe,CAAC,EAAE,MAAM,aAAa,CAAC;IAEtC;;OAEG;IACH,gBAAgB,CAAC,EAAE,MAAM,aAAa,CAAC;IAEvC;;OAEG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC;IAEpB;;OAEG;IACH,IAAI,CAAC,EAAE,QAAQ,CAAC;IAEhB;;OAEG;IACH,YAAY,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IAErC;;OAEG;IACH,KAAK,EAAE,MAAM,CAAC;IAEd;;;;OAIG;IACH,gCAAgC,CAAC,EAAE,OAAO,CAAC;IAE3C;;;;OAIG;IACH,2BAA2B,CAAC,EAAE,MAAM,CAAC;IAErC;;OAEG;IACH,WAAW,CAAC,EAAE,MAAM,EAAE,CAAC;IAEvB;;;OAGG;IACH,4BAA4B,CAAC,EAAE,OAAO,CAAC;IAEvC;;;OAGG;IACH,+BAA+B,CAAC,EAAE,OAAO,CAAC;IAE1C;;;OAGG;IACH,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AC9FD,OAAO,MAAM;;;;;CAKZ,CAAC;ACHF,cAAc;IACZ,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,+BAA+B,CAAC,EAAE,OAAO,CAAC;IAC1C,IAAI,CAAC,EAAE,QAAQ,CAAC;CACjB,CAAC;AAEF,OAAO,MAAM,oCACL,MAAM,kCAEX,OAAO,CAAC,MAAM,EAAE,CA6BlB,CAAC;ACvBF,OAAO,MAAM,oVA0BV,uBAAuB,KAAG,IAAI,OAwPhC,CAAC","sources":["src/src/util/touch-util.ts","src/src/util/caret-util.ts","src/src/util/index.ts","src/src/types/Language.ts","src/src/interfaces/Props.ts","src/src/constants/TriggerKeys.ts","src/src/util/suggestions-util.ts","src/src/index.tsx","src/index.tsx"],"sourcesContent":[null,null,null,null,null,null,null,null,"import * as React from \"react\";\nimport { useEffect, useRef, useState, useMemo } from \"react\";\nimport { setCaretPosition, getInputSelection, isTouchEnabled } from \"./util\";\nimport getCaretCoordinates from \"textarea-caret\";\nimport classes from \"./styles.module.css\";\nimport { ReactTransliterateProps } from \"./interfaces/Props\";\nimport { Language } from \"./types/Language\";\nimport { TriggerKeys } from \"./constants/TriggerKeys\";\nimport { getTransliterateSuggestions } from \"./util/suggestions-util\";\n\nconst KEY_UP = \"ArrowUp\";\nconst KEY_DOWN = \"ArrowDown\";\nconst KEY_ESCAPE = \"Escape\";\n\nconst OPTION_LIST_Y_OFFSET = 10;\nconst OPTION_LIST_MIN_WIDTH = 100;\n\nexport const ReactTransliterate = ({\n  renderComponent = (props) => <input {...props} />,\n  lang = \"hi\",\n  offsetX = 0,\n  offsetY = 10,\n  onChange,\n  onChangeText,\n  onBlur,\n  value,\n  onKeyDown,\n  containerClassName = \"\",\n  containerStyles = {},\n  activeItemStyles = {},\n  maxOptions = 5,\n  hideSuggestionBoxOnMobileDevices = false,\n  hideSuggestionBoxBreakpoint = 450,\n  triggerKeys = [\n    TriggerKeys.KEY_SPACE,\n    TriggerKeys.KEY_ENTER,\n    TriggerKeys.KEY_RETURN,\n    TriggerKeys.KEY_TAB,\n  ],\n  insertCurrentSelectionOnBlur = true,\n  showCurrentWordAsLastSuggestion = true,\n  enabled = true,\n  ...rest\n}: ReactTransliterateProps): JSX.Element => {\n  const [options, setOptions] = useState<string[]>([]);\n  const [left, setLeft] = useState(0);\n  const [top, setTop] = useState(0);\n  const [selection, setSelection] = useState<number>(0);\n  const [matchStart, setMatchStart] = useState(-1);\n  const [matchEnd, setMatchEnd] = useState(-1);\n  const inputRef = useRef<HTMLInputElement>(null);\n  const [windowSize, setWindowSize] = useState({ width: 0, height: 0 });\n\n  const shouldRenderSuggestions = useMemo(\n    () =>\n      hideSuggestionBoxOnMobileDevices\n        ? windowSize.width > hideSuggestionBoxBreakpoint\n        : true,\n    [windowSize, hideSuggestionBoxBreakpoint, hideSuggestionBoxOnMobileDevices],\n  );\n\n  const reset = () => {\n    // reset the component\n    setSelection(0);\n    setOptions([]);\n  };\n\n  const handleSelection = (index: number) => {\n    const currentString = value;\n    // create a new string with the currently typed word\n    // replaced with the word in transliterated language\n    const newValue =\n      currentString.substring(0, matchStart) +\n      options[index] +\n      \" \" +\n      currentString.substring(matchEnd + 1, currentString.length);\n\n    // set the position of the caret (cursor) one character after the\n    // the position of the new word\n    setTimeout(() => {\n      setCaretPosition(\n        // eslint-disable-next-line @typescript-eslint/no-non-null-assertion\n        inputRef.current!,\n        matchStart + options[index].length + 1,\n      );\n    }, 1);\n\n    // bubble up event to the parent component\n    const e = {\n      target: { value: newValue },\n    } as unknown as React.FormEvent<HTMLInputElement>;\n    onChangeText(newValue);\n    onChange && onChange(e);\n    reset();\n    return inputRef.current?.focus();\n  };\n\n  const renderSuggestions = async (lastWord: string) => {\n    if (!shouldRenderSuggestions) {\n      return;\n    }\n    // fetch suggestion from api\n    // const url = `https://www.google.com/inputtools/request?ime=transliteration_en_${lang}&num=5&cp=0&cs=0&ie=utf-8&oe=utf-8&app=jsapi&text=${lastWord}`;\n\n    const numOptions = showCurrentWordAsLastSuggestion\n      ? maxOptions - 1\n      : maxOptions;\n\n    const data = await getTransliterateSuggestions(lastWord, {\n      numOptions,\n      showCurrentWordAsLastSuggestion,\n      lang,\n    });\n    setOptions(data);\n  };\n\n  const handleChange = (e: React.FormEvent<HTMLInputElement>) => {\n    const value = e.currentTarget.value;\n\n    // bubble up event to the parent component\n    onChange && onChange(e);\n    onChangeText(value);\n\n    if (!shouldRenderSuggestions) {\n      return;\n    }\n\n    // get the current index of the cursor\n    const caret = getInputSelection(e.target as HTMLInputElement).end;\n    const input = inputRef.current;\n\n    if (!input) return;\n\n    const caretPos = getCaretCoordinates(input, caret);\n\n    // search for the last occurence of the space character from\n    // the cursor\n    const indexOfLastSpace =\n      value.lastIndexOf(\" \", caret - 1) < value.lastIndexOf(\"\\n\", caret - 1)\n        ? value.lastIndexOf(\"\\n\", caret - 1)\n        : value.lastIndexOf(\" \", caret - 1);\n\n    // first character of the currently being typed word is\n    // one character after the space character\n    // index of last character is one before the current position\n    // of the caret\n    setMatchStart(indexOfLastSpace + 1);\n    setMatchEnd(caret - 1);\n\n    // currentWord is the word that is being typed\n    const currentWord = value.slice(indexOfLastSpace + 1, caret);\n    if (currentWord && enabled) {\n      // make an api call to fetch suggestions\n      renderSuggestions(currentWord);\n\n      const rect = input.getBoundingClientRect();\n\n      // calculate new left and top of the suggestion list\n\n      // minimum of the caret position in the text input and the\n      // width of the text input\n      const left = Math.min(\n        caretPos.left,\n        rect.width - OPTION_LIST_MIN_WIDTH / 2,\n      );\n\n      // minimum of the caret position from the top of the input\n      // and the height of the input\n      const top = Math.min(caretPos.top + OPTION_LIST_Y_OFFSET, rect.height);\n\n      setTop(top);\n      setLeft(left);\n    } else {\n      reset();\n    }\n  };\n\n  const handleKeyDown = (event: React.KeyboardEvent<HTMLInputElement>) => {\n    const helperVisible = options.length > 0;\n\n    if (helperVisible) {\n      if (triggerKeys.includes(event.key)) {\n        event.preventDefault();\n        handleSelection(selection);\n      } else {\n        switch (event.key) {\n          case KEY_ESCAPE:\n            event.preventDefault();\n            reset();\n            break;\n          case KEY_UP:\n            event.preventDefault();\n            setSelection((options.length + selection - 1) % options.length);\n            break;\n          case KEY_DOWN:\n            event.preventDefault();\n            setSelection((selection + 1) % options.length);\n            break;\n          default:\n            onKeyDown && onKeyDown(event);\n            break;\n        }\n      }\n    } else {\n      onKeyDown && onKeyDown(event);\n    }\n  };\n\n  const handleBlur = (\n    event: React.FocusEvent<HTMLInputElement | HTMLTextAreaElement>,\n  ) => {\n    if (!isTouchEnabled()) {\n      if (insertCurrentSelectionOnBlur && options[selection]) {\n        handleSelection(selection);\n      } else {\n        reset();\n      }\n    }\n    onBlur && onBlur(event);\n  };\n\n  const handleResize = () => {\n    // TODO implement the resize function to resize\n    // the helper on screen size change\n    const width = window.innerWidth;\n    const height = window.innerHeight;\n    setWindowSize({ width, height });\n  };\n\n  useEffect(() => {\n    window.addEventListener(\"resize\", handleResize);\n    const width = window.innerWidth;\n    const height = window.innerHeight;\n    setWindowSize({ width, height });\n\n    return () => {\n      window.removeEventListener(\"resize\", handleResize);\n    };\n  }, []);\n\n  return (\n    <div\n      // position relative is required to show the component\n      // in the correct position\n      style={{\n        ...containerStyles,\n        position: \"relative\",\n      }}\n      className={containerClassName}\n    >\n      {renderComponent({\n        onChange: handleChange,\n        onKeyDown: handleKeyDown,\n        onBlur: handleBlur,\n        ref: inputRef,\n        value: value,\n        \"data-testid\": \"rt-input-component\",\n        ...rest,\n      })}\n      {shouldRenderSuggestions && options.length > 0 && (\n        <ul\n          style={{\n            left: `${left + offsetX}px`,\n            top: `${top + offsetY}px`,\n            position: \"absolute\",\n            width: \"auto\",\n          }}\n          className={classes.ReactTransliterate}\n          data-testid=\"rt-suggestions-list\"\n        >\n          {/*\n           * convert to set and back to prevent duplicate list items\n           * that might happen while using backspace\n           */}\n          {Array.from(new Set(options)).map((item, index) => (\n            <li\n              className={index === selection ? classes.Active : undefined}\n              style={index === selection ? activeItemStyles || {} : {}}\n              onMouseEnter={() => {\n                setSelection(index);\n              }}\n              onClick={() => handleSelection(index)}\n              key={item}\n            >\n              {item}\n            </li>\n          ))}\n        </ul>\n      )}\n    </div>\n  );\n};\n\nexport type { ReactTransliterateProps, Language };\nexport { TriggerKeys, getTransliterateSuggestions };\n"],"names":[],"version":3,"file":"types.d.ts.map","sourceRoot":"../"}