marathi_typing 0.1.3 → 0.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 3f7da6deb5a3d1bd52b560302e0e92b442c5932f73894c966381c7241125c18e
4
- data.tar.gz: c56dbcc87be46f67e6d35107ecfb148280422903a86f1dd76b453cef6bd4f99f
3
+ metadata.gz: bea1ae0ad19188d8d72b79dd93e2458edcb7815de0b9a2eef7a068467cae9ad2
4
+ data.tar.gz: 0df203a4cc8a8d1e6b5a62684c38b116ccaa657b80c8cc1419b6b88c5d1d3e4d
5
5
  SHA512:
6
- metadata.gz: 74094b83da6d95f2706932319064aa9381b6dc8cdd50b792ed434f0d3826e40589ad94a3b0a4623aac0840a780929c3a0a5ea3ccfa14c10148b78dd82421dee4
7
- data.tar.gz: 5acfb3c4a6dcd827865956863229ea4c147663e01123e9f47964486438d43735d99b175c479126047e05732aefc00a36253a8b9e9cef5e44c267ceb068fd033f
6
+ metadata.gz: dc082fc3eaaac92ecee8282cda4fb7adbdc8264ab33594f7888bef997f6968e074c207015f12ad918f70685481563ea2113d1f668e57c130583811b021ba8d75
7
+ data.tar.gz: b24417a8cfc3a182135f0f37144d074f4d8adb43ccd648595e761f532f75c4a01c684f7cf5ab5c6b115ac89168b21846a255f65b93dafdc69a14f2fdb9823132
@@ -1,9 +1,9 @@
1
1
  import { Controller } from "@hotwired/stimulus"
2
2
 
3
3
  export default class extends Controller {
4
- static targets = ["input"]
5
- timeout = null
4
+ static targets = ["input"];
6
5
 
6
+ timeout = null
7
7
 
8
8
  connect() {
9
9
 
@@ -14,16 +14,13 @@ export default class extends Controller {
14
14
  this.suggestionsTarget = this.buildSuggestionsBox()
15
15
 
16
16
  input.addEventListener("keydown", (e) => {
17
-
18
17
  if (e.key === " ") {
19
18
  this.skipNextSuggestionFetch = true;
20
19
  }
21
20
  });
22
21
 
23
22
  input.addEventListener("input", (e) => {
24
-
25
23
  if (this.skipNextSuggestionFetch) {
26
-
27
24
  this.skipNextSuggestionFetch = false;
28
25
  return;
29
26
  }
@@ -33,13 +30,11 @@ export default class extends Controller {
33
30
  input.addEventListener("keyup", (e) => {
34
31
 
35
32
  if (e.key === " ") {
36
-
37
33
  this.selectFirstSuggestion();
38
34
  }
39
35
  });
40
36
 
41
37
  document.addEventListener("click", this.handleClickOutside);
42
-
43
38
  }
44
39
 
45
40
  fetchSuggestions(event) {
@@ -48,47 +43,54 @@ export default class extends Controller {
48
43
 
49
44
  clearTimeout(this.timeout)
50
45
  this.timeout = setTimeout(() => {
51
- const lastWord = value.split(" ").pop()
52
46
 
53
- fetch(`${this.apiUrl}?text=${lastWord}&itc=mr-t-i0-und&num=5&cp=0&cs=1&ie=utf-8&oe=utf-8&app=demopage`)
47
+ const { word: currentWord } = this.getCurrentWordAtCursor(this.inputTarget);
48
+
49
+ console.log("currentWord: ", currentWord)
50
+
51
+ if (!currentWord || !/^[a-zA-Z]+$/.test(currentWord)) return;
52
+
53
+ fetch(`${this.apiUrl}?text=${currentWord}&itc=mr-t-i0-und&num=5&cp=0&cs=1&ie=utf-8&oe=utf-8&app=demopage`)
54
54
  .then(res => res.json())
55
55
  .then(data => {
56
56
  if (data[0] === "SUCCESS") {
57
57
  const suggestions = data[1][0][1]
58
- this.showSuggestions(suggestions, lastWord)
58
+ this.showSuggestions(suggestions, currentWord)
59
59
  }
60
60
  })
61
61
  }, 300)
62
62
  }
63
63
 
64
64
  buildSuggestionsBox() {
65
-
66
- const box = document.createElement("div")
65
+ const box = document.createElement("div");
67
66
  box.className = "marathi-suggestions";
68
67
  box.style.position = "absolute";
69
- box.style.background = "#fff";
68
+ box.style.background = "rgb(239 238 236)";
70
69
  box.style.border = "0px solid #ccc";
71
70
  box.style.zIndex = "9999";
72
71
  box.style.padding = "0px";
73
72
  box.style.color = "black";
74
73
 
75
- // Get the position of the input field
74
+ // Get the input's position relative to the viewport
76
75
  const rect = this.inputTarget.getBoundingClientRect();
77
- const scrollOffset = window.scrollY || window.pageYOffset;
78
76
 
79
- box.style.top = `${rect.bottom + scrollOffset}px`;
80
- box.style.left = `${rect.left}px`; //
77
+ // Calculate position relative to the document
78
+ const scrollTop = window.scrollY || window.pageYOffset;
79
+ const scrollLeft = window.scrollX || window.pageXOffset;
80
+
81
+ // Set position just below the input
82
+ box.style.top = `${rect.bottom + scrollTop}px`;
83
+ box.style.left = `${rect.left + scrollLeft}px`;
81
84
 
82
- this.element.parentElement.appendChild(box)
85
+ // Append to body to avoid being affected by parent positioning
86
+ document.body.appendChild(box);
83
87
 
84
- return box
88
+ return box;
85
89
  }
86
90
 
87
- showSuggestions(suggestions, lastWord) {
88
- this.suggestionsTarget.innerHTML = ""
89
- this.suggestionsTarget.style.border = "1px solid #ccc";
90
- this.suggestionsTarget.style.border = "4px";
91
91
 
92
+ showSuggestions(suggestions, lastWord) {
93
+ this.showSuggestionsBox();
92
94
 
93
95
  suggestions.forEach(suggestion => {
94
96
  const option = document.createElement("div")
@@ -108,36 +110,48 @@ export default class extends Controller {
108
110
  }
109
111
 
110
112
  selectFirstSuggestion() {
111
-
112
113
  if (!this.suggestionsTarget || this.suggestionsTarget.children.length === 0) return;
113
114
 
114
115
  const firstOption = this.suggestionsTarget.querySelector(".marathi-suggestion");
115
116
 
116
117
  if (firstOption) {
117
118
  const suggestion = firstOption.textContent;
118
- const value = this.inputTarget.value.trim();
119
- const lastWord = value.split(" ").pop();
120
- this.selectSuggestion(suggestion, lastWord);
119
+ const input = this.inputTarget;
120
+ let { word: currentWord } = this.getCurrentWordAtCursor(input);
121
+
122
+ // If space was pressed, caret is after the space => no current word
123
+ if (!currentWord) {
124
+ const valueBeforeCursor = input.value.slice(0, input.selectionStart).trimEnd();
125
+ const words = valueBeforeCursor.split(/\s+/);
126
+ currentWord = words[words.length - 1] || "";
127
+ }
128
+
129
+ if (!currentWord) return;
130
+
131
+ this.selectSuggestion(suggestion, currentWord);
121
132
  }
122
133
  }
123
134
 
135
+ selectSuggestion(selected, originalWord) {
136
+ const input = this.inputTarget;
137
+ const { wordStart, wordEnd } = this.getCurrentWordAtCursor(input);
124
138
 
125
- selectSuggestion(selected, lastWord) {
126
- let currentValue = this.inputTarget.value;
127
- const trailingSpace = currentValue.endsWith(" ") ? " " : "";
139
+ if (wordStart == null || wordEnd == null) return;
128
140
 
129
- // Remove trailing space temporarily
130
- currentValue = currentValue.trimEnd();
141
+ const value = input.value;
131
142
 
132
- // Replace the last word
133
- const newValue = currentValue.replace(new RegExp(`${lastWord}$`), selected);
143
+ // Reconstruct the value with the selected word
144
+ const newValue = value.slice(0, wordStart) + selected + value.slice(wordEnd);
134
145
 
135
- // Re-apply trailing space if needed
136
- this.inputTarget.value = newValue + trailingSpace;
146
+ // Update the input value and set caret after inserted suggestion
147
+ input.value = newValue;
148
+
149
+ // Move caret to after the inserted suggestion
150
+ const caretPos = wordStart + selected.length;
151
+ input.setSelectionRange(caretPos, caretPos);
152
+
153
+ this.hideSuggestions();
137
154
 
138
- this.suggestionsTarget.innerHTML = "";
139
- this.suggestionsTarget.style.border = "0";
140
- this.suggestionsTarget.style.padding = "0";
141
155
  }
142
156
 
143
157
  handleClickOutside = (event) => {
@@ -145,12 +159,65 @@ export default class extends Controller {
145
159
  const isClickInsideSuggestions = this.suggestionsTarget?.contains(event.target);
146
160
 
147
161
  if (!isClickInsideInput && !isClickInsideSuggestions) {
148
- this.suggestionsTarget.innerHTML = "";
149
- this.suggestionsTarget.style.border = "0";
150
- this.suggestionsTarget.style.padding = "0";
162
+ this.hideSuggestions();
151
163
  }
152
164
  };
153
165
 
154
166
 
155
- }
167
+ getCurrentWordAtCursor(input) {
168
+ const value = input.value;
169
+ let cursorPos = input.selectionStart;
170
+
171
+ // If cursor is just after a space, move it back to find previous word
172
+ if (cursorPos > 0 && value[cursorPos - 1] === " ") {
173
+ cursorPos--;
174
+ }
175
+
176
+ // Skip any Devanagari characters while going backwards
177
+ let wordStart = cursorPos;
178
+ while (
179
+ wordStart > 0 &&
180
+ value[wordStart - 1].match(/[a-zA-Z0-9]/) // Only count English letters and numbers
181
+ ) {
182
+ wordStart--;
183
+ }
184
+
185
+ // Skip Devanagari going forward too
186
+ let wordEnd = cursorPos;
187
+ while (
188
+ wordEnd < value.length &&
189
+ value[wordEnd].match(/[a-zA-Z0-9]/)
190
+ ) {
191
+ wordEnd++;
192
+ }
193
+
194
+
195
+ let word = value.slice(wordStart, wordEnd);
196
+
197
+ return { word, wordStart, wordEnd };
198
+ }
199
+
200
+ hideSuggestions() {
201
+ this.suggestionsTarget.innerHTML = "";
202
+ this.suggestionsTarget.style.border = "0";
203
+ this.suggestionsTarget.style.padding = "0";
204
+ }
205
+
206
+ showSuggestionsBox() {
207
+
208
+ const rect = this.inputTarget.getBoundingClientRect();
209
+
210
+ // Calculate position relative to the document
211
+ const scrollTop = window.scrollY || window.pageYOffset;
212
+ const scrollLeft = window.scrollX || window.pageXOffset;
213
+
214
+ // Set position just below the input
215
+ this.suggestionsTarget.style.top = `${rect.bottom + scrollTop}px`;
216
+ this.suggestionsTarget.style.left = `${rect.left + scrollLeft}px`;
217
+
218
+ this.suggestionsTarget.innerHTML = "";
219
+ this.suggestionsTarget.style.border = "1px solid #0a0a0a";
220
+ this.suggestionsTarget.style.padding = "4px";
221
+ }
156
222
 
223
+ }
@@ -1,3 +1,3 @@
1
1
  module MarathiTyping
2
- VERSION = '0.1.3'
2
+ VERSION = '0.2.1'
3
3
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: marathi_typing
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.3
4
+ version: 0.2.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ajit Dhanje
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2025-04-15 00:00:00.000000000 Z
11
+ date: 2025-04-17 00:00:00.000000000 Z
12
12
  dependencies: []
13
13
  description:
14
14
  email: