UNPKG

@ai4bharat/indic-transliterate

Version:
525 lines (501 loc) 22.5 kB
var $dWhh5$reactjsxruntime = require("react/jsx-runtime"); var $dWhh5$react = require("react"); var $dWhh5$textareacaret = require("textarea-caret"); function $parcel$interopDefault(a) { return a && a.__esModule ? a.default : a; } function $parcel$export(e, n, v, s) { Object.defineProperty(e, n, {get: v, set: s, enumerable: true, configurable: true}); } $parcel$export(module.exports, "IndicTransliterate", () => $3f93eb6d5bf69cb7$export$a62758b764e9e41d); $parcel$export(module.exports, "TriggerKeys", () => $9090cd5fdacb9284$export$24b0ea3375909d37); $parcel$export(module.exports, "getTransliterateSuggestions", () => $ad7f01abe8daa1b6$export$27f30d10c00bcc6c); $parcel$export(module.exports, "getTransliterationLanguages", () => $bb5a013037ca2153$export$58f2e270169de9d3); function $6a2317d46b969b19$export$e27e3030245d4c9b() { return "ontouchstart" in window || navigator.maxTouchPoints > 0 || navigator.msMaxTouchPoints > 0; } function $94c75e1d9a9e24f0$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 $94c75e1d9a9e24f0$export$97ab23b40042f8af(elem, caretPos) { if (elem) { if (elem.selectionStart) { elem.focus(); elem.setSelectionRange(caretPos, caretPos); } else elem.focus(); } } const $9090cd5fdacb9284$export$24b0ea3375909d37 = { KEY_RETURN: "Enter", KEY_ENTER: "Enter", KEY_TAB: "Tab", KEY_SPACE: " " }; const $ad7f01abe8daa1b6$var$MAX_CACHE_SIZE = 10000; const $ad7f01abe8daa1b6$var$SAVE_THRESHOLD = 20; const $ad7f01abe8daa1b6$var$CACHE_KEY = 'transliterationCache'; const $ad7f01abe8daa1b6$var$cache = $ad7f01abe8daa1b6$var$loadCacheFromLocalStorage(); let $ad7f01abe8daa1b6$var$newEntriesCount = 0; function $ad7f01abe8daa1b6$var$loadCacheFromLocalStorage() { const cachedData = localStorage.getItem($ad7f01abe8daa1b6$var$CACHE_KEY); return cachedData ? JSON.parse(cachedData) : { }; } function $ad7f01abe8daa1b6$var$saveCacheToLocalStorage() { localStorage.setItem($ad7f01abe8daa1b6$var$CACHE_KEY, JSON.stringify($ad7f01abe8daa1b6$var$cache)); } const $ad7f01abe8daa1b6$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 $ad7f01abe8daa1b6$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 (!$ad7f01abe8daa1b6$var$cache[lang]) $ad7f01abe8daa1b6$var$cache[lang] = { }; if ($ad7f01abe8daa1b6$var$cache[lang][word.toLowerCase()]) { $ad7f01abe8daa1b6$var$cache[lang][word.toLowerCase()].frequency += 1; return $ad7f01abe8daa1b6$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($ad7f01abe8daa1b6$var$cache[lang]).length >= $ad7f01abe8daa1b6$var$MAX_CACHE_SIZE) { const lowestFreqWord = $ad7f01abe8daa1b6$var$getWordWithLowestFrequency($ad7f01abe8daa1b6$var$cache[lang]); if (lowestFreqWord) delete $ad7f01abe8daa1b6$var$cache[lang][lowestFreqWord]; } $ad7f01abe8daa1b6$var$cache[lang][word.toLowerCase()] = { suggestions: found, frequency: 1 }; $ad7f01abe8daa1b6$var$newEntriesCount += 1; if ($ad7f01abe8daa1b6$var$newEntriesCount >= $ad7f01abe8daa1b6$var$SAVE_THRESHOLD) { $ad7f01abe8daa1b6$var$saveCacheToLocalStorage(); $ad7f01abe8daa1b6$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', $ad7f01abe8daa1b6$var$saveCacheToLocalStorage); const $905ee54827307cc1$export$ca6dda5263526f75 = "https://xlit-api.ai4bharat.org/"; const $905ee54827307cc1$export$a238c5e20ae27fe7 = "https://xlit-api.ai4bharat.org/tl/"; const $bb5a013037ca2153$export$58f2e270169de9d3 = async ()=>{ if (sessionStorage.getItem("indic_transliterate__supported_languages")) return JSON.parse(sessionStorage.getItem("indic_transliterate__supported_languages") || ""); else { const apiURL = `${$905ee54827307cc1$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 $3f93eb6d5bf69cb7$var$KEY_UP = "ArrowUp"; const $3f93eb6d5bf69cb7$var$KEY_DOWN = "ArrowDown"; const $3f93eb6d5bf69cb7$var$KEY_LEFT = "ArrowLeft"; const $3f93eb6d5bf69cb7$var$KEY_RIGHT = "ArrowRight"; const $3f93eb6d5bf69cb7$var$KEY_ESCAPE = "Escape"; const $3f93eb6d5bf69cb7$var$OPTION_LIST_Y_OFFSET = 10; const $3f93eb6d5bf69cb7$var$OPTION_LIST_MIN_WIDTH = 100; const $3f93eb6d5bf69cb7$export$a62758b764e9e41d = ({ renderComponent: renderComponent = (props)=>/*#__PURE__*/ $dWhh5$reactjsxruntime.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 = [ $9090cd5fdacb9284$export$24b0ea3375909d37.KEY_SPACE, $9090cd5fdacb9284$export$24b0ea3375909d37.KEY_ENTER, $9090cd5fdacb9284$export$24b0ea3375909d37.KEY_RETURN, $9090cd5fdacb9284$export$24b0ea3375909d37.KEY_TAB, ] , insertCurrentSelectionOnBlur: insertCurrentSelectionOnBlur = true , showCurrentWordAsLastSuggestion: showCurrentWordAsLastSuggestion = true , enabled: enabled = true , horizontalView: horizontalView = false , customApiURL: customApiURL = $905ee54827307cc1$export$a238c5e20ae27fe7 , apiKey: apiKey = "" , ...rest })=>{ const [left, setLeft] = $dWhh5$react.useState(0); const [top, setTop] = $dWhh5$react.useState(0); const [selection, setSelection] = $dWhh5$react.useState(0); const [matchStart, setMatchStart] = $dWhh5$react.useState(-1); const [matchEnd, setMatchEnd] = $dWhh5$react.useState(-1); const inputRef = $dWhh5$react.useRef(null); const [windowSize, setWindowSize] = $dWhh5$react.useState({ width: 0, height: 0 }); const [direction, setDirection] = $dWhh5$react.useState("ltr"); const [googleFont, setGoogleFont] = $dWhh5$react.useState(null); const [options, setOptions] = $dWhh5$react.useState([]); const [logJsonArray, setLogJsonArray] = $dWhh5$react.useState([]); const [numSpaces, setNumSpaces] = $dWhh5$react.useState(0); const [parentUuid, setParentUuid] = $dWhh5$react.useState("0"); const [uuid, setUuid] = $dWhh5$react.useState(Math.random().toString(36).substr(2, 9)); const [subStrLength, setSubStrLength] = $dWhh5$react.useState(0); const [restart, setRestart] = $dWhh5$react.useState(true); const shouldRenderSuggestions = $dWhh5$react.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(()=>{ $94c75e1d9a9e24f0$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 $ad7f01abe8daa1b6$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 $bb5a013037ca2153$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 = $94c75e1d9a9e24f0$export$8a4ff65f970d59a5(e.target).end; const input = inputRef.current; if (!input) return; const caretPos = $parcel$interopDefault($dWhh5$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 - $3f93eb6d5bf69cb7$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 + $3f93eb6d5bf69cb7$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 $3f93eb6d5bf69cb7$var$KEY_ESCAPE: event.preventDefault(); reset(); break; case $3f93eb6d5bf69cb7$var$KEY_UP: event.preventDefault(); setSelection((options.length + selection - 1) % options.length); break; case $3f93eb6d5bf69cb7$var$KEY_DOWN: event.preventDefault(); setSelection((selection + 1) % options.length); break; case $3f93eb6d5bf69cb7$var$KEY_LEFT: event.preventDefault(); setSelection((options.length + selection - 1) % options.length); break; case $3f93eb6d5bf69cb7$var$KEY_RIGHT: event.preventDefault(); setSelection((selection + 1) % options.length); break; default: onKeyDown && onKeyDown(event); break; } } else onKeyDown && onKeyDown(event); }; const handleBlur = (event)=>{ if (!$6a2317d46b969b19$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 }); }; $dWhh5$react.useEffect(()=>{ window.addEventListener("resize", handleResize); const width = window.innerWidth; const height = window.innerHeight; setWindowSize({ width: width, height: height }); return ()=>{ window.removeEventListener("resize", handleResize); }; }, []); $dWhh5$react.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__*/ $dWhh5$reactjsxruntime.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__*/ $dWhh5$reactjsxruntime.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__*/ $dWhh5$reactjsxruntime.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) ) }) ] })); }; //# sourceMappingURL=index.js.map