{"mappings":"AGAA,uBAAuB,MAAM,CAAC;ACE9B,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;IAElB;;;OAGG;IACH,cAAc,CAAC,EAAE,OAAO,CAAC;IAEzB,YAAY,CAAC,EAAE,MAAM,CAAC;IAEtB,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;ACtGD,kBAAyB;IACvB,MAAM,EAAE,MAAM,CAAC;IACf,YAAY,EAAE,MAAM,CAAC;IACrB,SAAS,EAAE,KAAK,GAAG,KAAK,CAAC;IACzB,WAAW,EAAE,MAAM,CAAC;IACpB,UAAU,EAAE,QAAQ,CAAC;IACrB,QAAQ,EAAE,OAAO,CAAC;IAClB,QAAQ,EAAE,QAAQ,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;IACnB,YAAY,EAAE,MAAM,CAAC;CACtB,CAAC;ACZF,OAAO,MAAM;;;;;CAKZ,CAAC;ACHF,cAAc;IAEZ,+BAA+B,CAAC,EAAE,OAAO,CAAC;IAC1C,IAAI,CAAC,EAAE,QAAQ,CAAC;CACjB,CAAC;AAqCF,OAAO,MAAM,oCACL,MAAM,gBACE,MAAM,UACZ,MAAM,kCAEb,OAAO,CAAC,MAAM,EAAE,GAAG,SAAS,CA8E9B,CAAC;AE3HF,OAAO,MAAM,mCAAwC,OAAO,CAC1D,UAAU,EAAE,GAAG,SAAS,CA2BzB,CAAC;ACVF,OAAO,MAAM,0XA6BV,uBAAuB,KAAG,IAAI,OA2XhC,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/types/LangObject.ts","src/src/constants/TriggerKeys.ts","src/src/util/suggestions-util.ts","src/src/constants/Urls.ts","src/src/util/getTransliterationLanguages.ts","src/src/index.tsx","src/index.tsx"],"sourcesContent":[null,null,null,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 { IndicTransliterateProps } from \"./interfaces/Props\";\nimport { Language } from \"./types/Language\";\nimport { LangObject } from \"./types/LangObject\";\nimport { TriggerKeys } from \"./constants/TriggerKeys\";\nimport { getTransliterateSuggestions } from \"./util/suggestions-util\";\nimport { getTransliterationLanguages } from \"./util/getTransliterationLanguages\";\nimport { BASE_URL_TL } from \"./constants/Urls\";\n\nconst KEY_UP = \"ArrowUp\";\nconst KEY_DOWN = \"ArrowDown\";\nconst KEY_LEFT = \"ArrowLeft\";\nconst KEY_RIGHT = \"ArrowRight\";\nconst KEY_ESCAPE = \"Escape\";\n\nconst OPTION_LIST_Y_OFFSET = 10;\nconst OPTION_LIST_MIN_WIDTH = 100;\n\nexport const IndicTransliterate = ({\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  horizontalView = false,\n  customApiURL = BASE_URL_TL,\n  apiKey = \"\",\n  ...rest\n}: IndicTransliterateProps): JSX.Element => {\n  interface LogJson {\n    keystrokes: any;\n    results: any;\n    opted: any;\n    created_at: any;\n    language: any;\n  }\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  const [direction, setDirection] = useState(\"ltr\");\n  const [googleFont, setGoogleFont] = useState<string | null>(null);\n  const [options, setOptions] = useState<string[]>([]);\n  const [logJsonArray, setLogJsonArray] = useState<LogJson[]>([]);\n  const [numSpaces, setNumSpaces] = useState(0);\n  const [parentUuid, setParentUuid] = useState(\"0\");\n  const [uuid, setUuid] = useState(Math.random().toString(36).substr(2, 9));\n  const [subStrLength, setSubStrLength] = useState(0);\n  const [restart, setRestart] = useState(true);\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    if(logJsonArray.length){\n      let lastLogJson = logJsonArray[logJsonArray.length-1];\n      let logJson:LogJson = {\n        keystrokes: lastLogJson.keystrokes,\n        results: lastLogJson.results,\n        opted: options[index],\n        created_at: new Date().toISOString(),\n        language: lang};\n      setLogJsonArray([...logJsonArray, logJson]);\n      setNumSpaces(numSpaces+1);\n    }\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, wholeText: 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, customApiURL, apiKey, {\n      // numOptions,\n      showCurrentWordAsLastSuggestion,\n      lang,\n    });\n    setOptions(data ?? []);\n    let logJson:LogJson = {\n              keystrokes: wholeText,\n              results: data,\n              opted: \"\",\n              created_at: new Date().toISOString(),\n              language: lang}\n\n    if(restart){\n      setRestart(false);\n      setLogJsonArray([logJson]);\n    }else{\n      setLogJsonArray([...logJsonArray, logJson]);\n    }\n  };\n\n  const getDirectionAndFont = async (lang: Language) => {\n    const langList = await getTransliterationLanguages();\n    const langObj = langList?.find((l) => l.LangCode === lang) as LangObject;\n    return [\n      langObj?.Direction ?? \"ltr\",\n      langObj?.GoogleFont,\n      langObj?.FallbackFont,\n    ];\n  };\n\n  const handleChange = (e: React.FormEvent<HTMLInputElement>) => {\n    const value = e.currentTarget.value;\n\n    if(numSpaces == 0 || restart){\n      if(value.length >= 4){\n      setSubStrLength(value.length-4);\n      }else{\n      setSubStrLength(0);\n      }\n    } \n\n    if (numSpaces >= 5){\n      const finalJson = {\"uuid\": uuid, \"parent_uuid\": parentUuid, \"word\": value, \"source\": localStorage.getItem('source') != undefined ? localStorage.getItem('source') : \"node-module\", \"language\": lang, \"steps\":logJsonArray};\n      setLogJsonArray([]);\n      setParentUuid(uuid);\n      setUuid(Math.random().toString(36).substr(2, 9));\n      setSubStrLength(value.length-2);\n      setNumSpaces(0);\n      setRestart(true);\n      fetch(\"https://backend.shoonya.ai4bharat.org/logs/transliteration_selection/\", {\n        method: \"POST\",\n        body: JSON.stringify(finalJson),\n        headers: {\n          \"Content-Type\": \"application/json\"\n        },\n      })\n      .then(async (res) => {\n        if (!res.ok) {throw await res.json()};\n      })\n      .catch((err) => {\n        console.log(\"error\", err);\n      });\n    }\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    if(numSpaces == 0 || restart){\n      if(value.length >= 4){\n      renderSuggestions(currentWord, value.substr(value.length-4, value.length));\n      }else{\n      renderSuggestions(currentWord, value.substr(0, value.length));\n      }\n    }else{\n      renderSuggestions(currentWord, value.substr(subStrLength, value.length));\n    }\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          case KEY_LEFT:\n            event.preventDefault();\n            setSelection((options.length + selection - 1) % options.length);\n            break;\n          case KEY_RIGHT:\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  useEffect(() => {\n    getDirectionAndFont(lang).then(([direction, googleFont, fallbackFont]) => {\n      setDirection(direction);\n      // import google font if not already imported\n      if (googleFont) {\n        if (!document.getElementById(`font-${googleFont}`)) {\n          const link = document.createElement(\"link\");\n          link.id = `font-${googleFont}`;\n          link.href = `https://fonts.googleapis.com/css?family=${googleFont}`;\n          link.rel = \"stylesheet\";\n          document.head.appendChild(link);\n        }\n        setGoogleFont(`${googleFont}, ${fallbackFont ?? \"sans-serif\"}`);\n      } else {\n        setGoogleFont(null);\n      }\n    });\n  }, [lang]);\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        lang: lang,\n        style: {\n          direction: direction,\n          ...(googleFont && { fontFamily: googleFont }),\n        },\n        ...rest,\n      })}\n      {shouldRenderSuggestions && options.length > 0 && (\n        <ul\n          style={{\n            backgroundClip : \"padding-box\",\n            backgroundColor : \"#fff\",\n            border : \"1px solid rgba(0, 0, 0, 0.15)\",\n            boxShadow : \"0 6px 12px rgba(0, 0, 0, 0.175)\",\n            display: horizontalView ? \"flex\" : \"block\",\n            fontSize: \"14px\",\n            listStyle: \"none\",\n            padding: \"1px\",\n            textAlign: \"center\",\n            zIndex: 20000,\n            left: `${left + offsetX}px`,\n            top: `${top + offsetY}px`,\n            position: \"absolute\",\n            width: \"auto\",\n            ...(googleFont && { fontFamily: googleFont }),\n          }}\n          data-testid=\"rt-suggestions-list\"\n          lang={lang}\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              style={index === selection ? { cursor: \"pointer\",padding: \"10px\",minWidth: \"100px\",backgroundColor: \"#65c3d7\", color:\"#fff\"} : { cursor: \"pointer\",padding: \"10px\",minWidth: \"100px\",backgroundColor: \"#fff\"} }\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 { IndicTransliterateProps, Language };\nexport { TriggerKeys, getTransliterateSuggestions };\nexport { getTransliterationLanguages };\n"],"names":[],"version":3,"file":"types.d.ts.map","sourceRoot":"../"}