UNPKG

@ai4bharat/indic-transliterate

Version:
515 lines (492 loc) 22.1 kB
import {jsx as $eSIqy$jsx, jsxs as $eSIqy$jsxs} from "react/jsx-runtime"; import {useState as $eSIqy$useState, useRef as $eSIqy$useRef, useMemo as $eSIqy$useMemo, useEffect as $eSIqy$useEffect} from "react"; import $eSIqy$textareacaret from "textarea-caret"; function $19eb910254214610$export$e27e3030245d4c9b() { return "ontouchstart" in window || navigator.maxTouchPoints > 0 || navigator.msMaxTouchPoints > 0; } function $2f5cf912a7dc4b84$export$8a4ff65f970d59a5(el) { const start = 0; const end = 0; if (!el) return { start: start, end: end }; if (typeof el.selectionStart === "number" && typeof el.selectionEnd === "number") return { start: el.selectionStart, end: el.selectionEnd }; return { start: start, end: end }; } function $2f5cf912a7dc4b84$export$97ab23b40042f8af(elem, caretPos) { if (elem) { if (elem.selectionStart) { elem.focus(); elem.setSelectionRange(caretPos, caretPos); } else elem.focus(); } } const $5ac81081e5c28bfa$export$24b0ea3375909d37 = { KEY_RETURN: "Enter", KEY_ENTER: "Enter", KEY_TAB: "Tab", KEY_SPACE: " " }; const $69c8f257da8dc8b1$var$MAX_CACHE_SIZE = 10000; const $69c8f257da8dc8b1$var$SAVE_THRESHOLD = 20; const $69c8f257da8dc8b1$var$CACHE_KEY = 'transliterationCache'; const $69c8f257da8dc8b1$var$cache = $69c8f257da8dc8b1$var$loadCacheFromLocalStorage(); let $69c8f257da8dc8b1$var$newEntriesCount = 0; function $69c8f257da8dc8b1$var$loadCacheFromLocalStorage() { const cachedData = localStorage.getItem($69c8f257da8dc8b1$var$CACHE_KEY); return cachedData ? JSON.parse(cachedData) : { }; } function $69c8f257da8dc8b1$var$saveCacheToLocalStorage() { localStorage.setItem($69c8f257da8dc8b1$var$CACHE_KEY, JSON.stringify($69c8f257da8dc8b1$var$cache)); } const $69c8f257da8dc8b1$var$getWordWithLowestFrequency = (dictionary)=>{ let lowestFreqWord = null; let lowestFreq = Infinity; for(const word in dictionary)if (dictionary[word].frequency < lowestFreq) { lowestFreq = dictionary[word].frequency; lowestFreqWord = word; } return lowestFreqWord; }; const $69c8f257da8dc8b1$export$27f30d10c00bcc6c = async (word, customApiURL, apiKey, config)=>{ const { showCurrentWordAsLastSuggestion: // numOptions = 5, showCurrentWordAsLastSuggestion = true , lang: lang = "hi" , } = config || { }; // fetch suggestion from api // 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=${word}`; // let myHeaders = new Headers(); // myHeaders.append("Content-Type", "application/json"); if (!$69c8f257da8dc8b1$var$cache[lang]) $69c8f257da8dc8b1$var$cache[lang] = { }; if ($69c8f257da8dc8b1$var$cache[lang][word.toLowerCase()]) { $69c8f257da8dc8b1$var$cache[lang][word.toLowerCase()].frequency += 1; return $69c8f257da8dc8b1$var$cache[lang][word.toLowerCase()].suggestions; } const requestOptions = { method: "GET", headers: { "Authorization": apiKey } }; try { const res = await fetch(customApiURL + `${lang}/${word === "." || word === ".." ? " " + word.replace(".", "%2E") : encodeURIComponent(word).replace(".", "%2E")}`, requestOptions); let data = await res.json(); console.log("library data", data); if (!customApiURL.includes("xlit-api")) data.result = data.output[0].target; if (data && data.result.length > 0) { const found = showCurrentWordAsLastSuggestion ? [ ...data.result, word ] : data.result; if (Object.keys($69c8f257da8dc8b1$var$cache[lang]).length >= $69c8f257da8dc8b1$var$MAX_CACHE_SIZE) { const lowestFreqWord = $69c8f257da8dc8b1$var$getWordWithLowestFrequency($69c8f257da8dc8b1$var$cache[lang]); if (lowestFreqWord) delete $69c8f257da8dc8b1$var$cache[lang][lowestFreqWord]; } $69c8f257da8dc8b1$var$cache[lang][word.toLowerCase()] = { suggestions: found, frequency: 1 }; $69c8f257da8dc8b1$var$newEntriesCount += 1; if ($69c8f257da8dc8b1$var$newEntriesCount >= $69c8f257da8dc8b1$var$SAVE_THRESHOLD) { $69c8f257da8dc8b1$var$saveCacheToLocalStorage(); $69c8f257da8dc8b1$var$newEntriesCount = 0; } return found; } else { if (showCurrentWordAsLastSuggestion) { const fallback = [ word ]; return fallback; } return []; } } catch (e) { // catch error console.error("There was an error with transliteration", e); return []; } }; window.addEventListener('beforeunload', $69c8f257da8dc8b1$var$saveCacheToLocalStorage); const $b9b628447857a10a$export$ca6dda5263526f75 = "https://xlit-api.ai4bharat.org/"; const $b9b628447857a10a$export$a238c5e20ae27fe7 = "https://xlit-api.ai4bharat.org/tl/"; const $d8161b358c525845$export$58f2e270169de9d3 = async ()=>{ if (sessionStorage.getItem("indic_transliterate__supported_languages")) return JSON.parse(sessionStorage.getItem("indic_transliterate__supported_languages") || ""); else { const apiURL = `${$b9b628447857a10a$export$ca6dda5263526f75}languages`; const myHeaders = new Headers(); myHeaders.append("Content-Type", "application/json"); const requestOptions = { method: "GET" }; try { const res = await fetch(apiURL, requestOptions); const data = await res.json(); sessionStorage.setItem("indic_transliterate__supported_languages", JSON.stringify(data)); return data; } catch (e) { console.error("There was an error with transliteration", e); return []; } } }; const $41d49c8a6078fe3c$var$KEY_UP = "ArrowUp"; const $41d49c8a6078fe3c$var$KEY_DOWN = "ArrowDown"; const $41d49c8a6078fe3c$var$KEY_LEFT = "ArrowLeft"; const $41d49c8a6078fe3c$var$KEY_RIGHT = "ArrowRight"; const $41d49c8a6078fe3c$var$KEY_ESCAPE = "Escape"; const $41d49c8a6078fe3c$var$OPTION_LIST_Y_OFFSET = 10; const $41d49c8a6078fe3c$var$OPTION_LIST_MIN_WIDTH = 100; const $41d49c8a6078fe3c$export$a62758b764e9e41d = ({ renderComponent: renderComponent = (props)=>/*#__PURE__*/ $eSIqy$jsx("input", { ...props }) , lang: lang = "hi" , offsetX: offsetX = 0 , offsetY: offsetY = 10 , onChange: onChange , onChangeText: onChangeText , onBlur: onBlur , value: value , onKeyDown: onKeyDown , containerClassName: containerClassName = "" , containerStyles: containerStyles = { } , activeItemStyles: activeItemStyles = { } , maxOptions: maxOptions = 5 , hideSuggestionBoxOnMobileDevices: hideSuggestionBoxOnMobileDevices = false , hideSuggestionBoxBreakpoint: hideSuggestionBoxBreakpoint = 450 , triggerKeys: triggerKeys = [ $5ac81081e5c28bfa$export$24b0ea3375909d37.KEY_SPACE, $5ac81081e5c28bfa$export$24b0ea3375909d37.KEY_ENTER, $5ac81081e5c28bfa$export$24b0ea3375909d37.KEY_RETURN, $5ac81081e5c28bfa$export$24b0ea3375909d37.KEY_TAB, ] , insertCurrentSelectionOnBlur: insertCurrentSelectionOnBlur = true , showCurrentWordAsLastSuggestion: showCurrentWordAsLastSuggestion = true , enabled: enabled = true , horizontalView: horizontalView = false , customApiURL: customApiURL = $b9b628447857a10a$export$a238c5e20ae27fe7 , apiKey: apiKey = "" , ...rest })=>{ const [left, setLeft] = $eSIqy$useState(0); const [top, setTop] = $eSIqy$useState(0); const [selection, setSelection] = $eSIqy$useState(0); const [matchStart, setMatchStart] = $eSIqy$useState(-1); const [matchEnd, setMatchEnd] = $eSIqy$useState(-1); const inputRef = $eSIqy$useRef(null); const [windowSize, setWindowSize] = $eSIqy$useState({ width: 0, height: 0 }); const [direction, setDirection] = $eSIqy$useState("ltr"); const [googleFont, setGoogleFont] = $eSIqy$useState(null); const [options, setOptions] = $eSIqy$useState([]); const [logJsonArray, setLogJsonArray] = $eSIqy$useState([]); const [numSpaces, setNumSpaces] = $eSIqy$useState(0); const [parentUuid, setParentUuid] = $eSIqy$useState("0"); const [uuid, setUuid] = $eSIqy$useState(Math.random().toString(36).substr(2, 9)); const [subStrLength, setSubStrLength] = $eSIqy$useState(0); const [restart, setRestart] = $eSIqy$useState(true); const shouldRenderSuggestions = $eSIqy$useMemo(()=>hideSuggestionBoxOnMobileDevices ? windowSize.width > hideSuggestionBoxBreakpoint : true , [ windowSize, hideSuggestionBoxBreakpoint, hideSuggestionBoxOnMobileDevices ]); const reset = ()=>{ // reset the component setSelection(0); setOptions([]); }; const handleSelection = (index)=>{ var ref; const currentString = value; // create a new string with the currently typed word // replaced with the word in transliterated language const newValue = currentString.substring(0, matchStart) + options[index] + " " + currentString.substring(matchEnd + 1, currentString.length); if (logJsonArray.length) { let lastLogJson = logJsonArray[logJsonArray.length - 1]; let logJson = { keystrokes: lastLogJson.keystrokes, results: lastLogJson.results, opted: options[index], created_at: new Date().toISOString(), language: lang }; setLogJsonArray([ ...logJsonArray, logJson ]); setNumSpaces(numSpaces + 1); } // set the position of the caret (cursor) one character after the // the position of the new word setTimeout(()=>{ $2f5cf912a7dc4b84$export$97ab23b40042f8af(// eslint-disable-next-line @typescript-eslint/no-non-null-assertion inputRef.current, matchStart + options[index].length + 1); }, 1); // bubble up event to the parent component const e = { target: { value: newValue } }; onChangeText(newValue); onChange && onChange(e); reset(); return (ref = inputRef.current) === null || ref === void 0 ? void 0 : ref.focus(); }; const renderSuggestions = async (lastWord, wholeText)=>{ if (!shouldRenderSuggestions) return; // fetch suggestion from api // 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}`; // const numOptions = showCurrentWordAsLastSuggestion // ? maxOptions - 1 // : maxOptions; const data = await $69c8f257da8dc8b1$export$27f30d10c00bcc6c(lastWord, customApiURL, apiKey, { showCurrentWordAsLastSuggestion: // numOptions, showCurrentWordAsLastSuggestion, lang: lang }); setOptions(data !== null && data !== void 0 ? data : []); let logJson = { keystrokes: wholeText, results: data, opted: "", created_at: new Date().toISOString(), language: lang }; if (restart) { setRestart(false); setLogJsonArray([ logJson ]); } else setLogJsonArray([ ...logJsonArray, logJson ]); }; const getDirectionAndFont = async (lang)=>{ const langList = await $d8161b358c525845$export$58f2e270169de9d3(); const langObj = langList === null || langList === void 0 ? void 0 : langList.find((l)=>l.LangCode === lang ); var ref; return [ (ref = langObj === null || langObj === void 0 ? void 0 : langObj.Direction) !== null && ref !== void 0 ? ref : "ltr", langObj === null || langObj === void 0 ? void 0 : langObj.GoogleFont, langObj === null || langObj === void 0 ? void 0 : langObj.FallbackFont, ]; }; const handleChange = (e)=>{ const value = e.currentTarget.value; if (numSpaces == 0 || restart) { if (value.length >= 4) setSubStrLength(value.length - 4); else setSubStrLength(0); } if (numSpaces >= 5) { const finalJson = { "uuid": uuid, "parent_uuid": parentUuid, "word": value, "source": localStorage.getItem('source') != undefined ? localStorage.getItem('source') : "node-module", "language": lang, "steps": logJsonArray }; setLogJsonArray([]); setParentUuid(uuid); setUuid(Math.random().toString(36).substr(2, 9)); setSubStrLength(value.length - 2); setNumSpaces(0); setRestart(true); fetch("https://backend.shoonya.ai4bharat.org/logs/transliteration_selection/", { method: "POST", body: JSON.stringify(finalJson), headers: { "Content-Type": "application/json" } }).then(async (res)=>{ if (!res.ok) throw await res.json(); }).catch((err)=>{ console.log("error", err); }); } // bubble up event to the parent component onChange && onChange(e); onChangeText(value); if (!shouldRenderSuggestions) return; // get the current index of the cursor const caret = $2f5cf912a7dc4b84$export$8a4ff65f970d59a5(e.target).end; const input = inputRef.current; if (!input) return; const caretPos = $eSIqy$textareacaret(input, caret); // search for the last occurence of the space character from // the cursor const indexOfLastSpace = value.lastIndexOf(" ", caret - 1) < value.lastIndexOf("\n", caret - 1) ? value.lastIndexOf("\n", caret - 1) : value.lastIndexOf(" ", caret - 1); // first character of the currently being typed word is // one character after the space character // index of last character is one before the current position // of the caret setMatchStart(indexOfLastSpace + 1); setMatchEnd(caret - 1); // currentWord is the word that is being typed const currentWord = value.slice(indexOfLastSpace + 1, caret); if (currentWord && enabled) { // make an api call to fetch suggestions if (numSpaces == 0 || restart) { if (value.length >= 4) renderSuggestions(currentWord, value.substr(value.length - 4, value.length)); else renderSuggestions(currentWord, value.substr(0, value.length)); } else renderSuggestions(currentWord, value.substr(subStrLength, value.length)); const rect = input.getBoundingClientRect(); // calculate new left and top of the suggestion list // minimum of the caret position in the text input and the // width of the text input const left = Math.min(caretPos.left, rect.width - $41d49c8a6078fe3c$var$OPTION_LIST_MIN_WIDTH / 2); // minimum of the caret position from the top of the input // and the height of the input const top = Math.min(caretPos.top + $41d49c8a6078fe3c$var$OPTION_LIST_Y_OFFSET, rect.height); setTop(top); setLeft(left); } else reset(); }; const handleKeyDown = (event)=>{ const helperVisible = options.length > 0; if (helperVisible) { if (triggerKeys.includes(event.key)) { event.preventDefault(); handleSelection(selection); } else switch(event.key){ case $41d49c8a6078fe3c$var$KEY_ESCAPE: event.preventDefault(); reset(); break; case $41d49c8a6078fe3c$var$KEY_UP: event.preventDefault(); setSelection((options.length + selection - 1) % options.length); break; case $41d49c8a6078fe3c$var$KEY_DOWN: event.preventDefault(); setSelection((selection + 1) % options.length); break; case $41d49c8a6078fe3c$var$KEY_LEFT: event.preventDefault(); setSelection((options.length + selection - 1) % options.length); break; case $41d49c8a6078fe3c$var$KEY_RIGHT: event.preventDefault(); setSelection((selection + 1) % options.length); break; default: onKeyDown && onKeyDown(event); break; } } else onKeyDown && onKeyDown(event); }; const handleBlur = (event)=>{ if (!$19eb910254214610$export$e27e3030245d4c9b()) { if (insertCurrentSelectionOnBlur && options[selection]) handleSelection(selection); else reset(); } onBlur && onBlur(event); }; const handleResize = ()=>{ // TODO implement the resize function to resize // the helper on screen size change const width = window.innerWidth; const height = window.innerHeight; setWindowSize({ width: width, height: height }); }; $eSIqy$useEffect(()=>{ window.addEventListener("resize", handleResize); const width = window.innerWidth; const height = window.innerHeight; setWindowSize({ width: width, height: height }); return ()=>{ window.removeEventListener("resize", handleResize); }; }, []); $eSIqy$useEffect(()=>{ getDirectionAndFont(lang).then(([direction, googleFont, fallbackFont])=>{ setDirection(direction); // import google font if not already imported if (googleFont) { if (!document.getElementById(`font-${googleFont}`)) { const link = document.createElement("link"); link.id = `font-${googleFont}`; link.href = `https://fonts.googleapis.com/css?family=${googleFont}`; link.rel = "stylesheet"; document.head.appendChild(link); } setGoogleFont(`${googleFont}, ${fallbackFont !== null && fallbackFont !== void 0 ? fallbackFont : "sans-serif"}`); } else setGoogleFont(null); }); }, [ lang ]); return(/*#__PURE__*/ $eSIqy$jsxs("div", { // position relative is required to show the component // in the correct position style: { ...containerStyles, position: "relative" }, className: containerClassName, children: [ renderComponent({ onChange: handleChange, onKeyDown: handleKeyDown, onBlur: handleBlur, ref: inputRef, value: value, "data-testid": "rt-input-component", lang: lang, style: { direction: direction, ...googleFont && { fontFamily: googleFont } }, ...rest }), shouldRenderSuggestions && options.length > 0 && /*#__PURE__*/ $eSIqy$jsx("ul", { style: { backgroundClip: "padding-box", backgroundColor: "#fff", border: "1px solid rgba(0, 0, 0, 0.15)", boxShadow: "0 6px 12px rgba(0, 0, 0, 0.175)", display: horizontalView ? "flex" : "block", fontSize: "14px", listStyle: "none", padding: "1px", textAlign: "center", zIndex: 20000, left: `${left + offsetX}px`, top: `${top + offsetY}px`, position: "absolute", width: "auto", ...googleFont && { fontFamily: googleFont } }, "data-testid": "rt-suggestions-list", lang: lang, children: Array.from(new Set(options)).map((item, index)=>/*#__PURE__*/ $eSIqy$jsx("li", { style: index === selection ? { cursor: "pointer", padding: "10px", minWidth: "100px", backgroundColor: "#65c3d7", color: "#fff" } : { cursor: "pointer", padding: "10px", minWidth: "100px", backgroundColor: "#fff" }, onMouseEnter: ()=>{ setSelection(index); }, onClick: ()=>handleSelection(index) , children: item }, item) ) }) ] })); }; export {$41d49c8a6078fe3c$export$a62758b764e9e41d as IndicTransliterate, $5ac81081e5c28bfa$export$24b0ea3375909d37 as TriggerKeys, $69c8f257da8dc8b1$export$27f30d10c00bcc6c as getTransliterateSuggestions, $d8161b358c525845$export$58f2e270169de9d3 as getTransliterationLanguages}; //# sourceMappingURL=index.modern.js.map