@momo-kits/calculator-keyboard 0.150.2-beta.3 → 0.150.2-beta.30
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.
- package/README.md +45 -3
- package/android/src/main/java/com/calculatorkeyboard/CalculatorKeyboardPackage.kt +1 -1
- package/android/src/main/java/com/calculatorkeyboard/CustomKeyboardView.kt +225 -90
- package/android/src/main/java/com/calculatorkeyboard/Event.kt +36 -0
- package/android/src/main/java/com/calculatorkeyboard/InputCalculatorViewManager.kt +270 -0
- package/android/src/main/java/com/calculatorkeyboard/KeyboardOverplayHost.kt +232 -0
- package/ios/CalculatorKeyboardView.h +30 -0
- package/ios/CalculatorKeyboardView.mm +231 -0
- package/ios/NativeInputCalculator.h +11 -0
- package/ios/NativeInputCalculator.mm +369 -0
- package/package.json +21 -131
- package/react-native-calculator-keyboard.podspec +5 -4
- package/src/InputCalculatorNativeComponent.ts +62 -0
- package/src/index.tsx +104 -31
- package/android/src/main/java/com/calculatorkeyboard/RCTInputCalculator.kt +0 -179
- package/ios/CalculatorKeyboardView.swift +0 -115
- package/ios/InputCalculator-Bridging-Header.h +0 -23
- package/ios/InputCalculator.m +0 -79
- package/ios/InputCalculator.swift +0 -138
- package/ios/extension.swift +0 -23
- package/lib/commonjs/index.js +0 -48
- package/lib/commonjs/index.js.map +0 -1
- package/lib/module/index.js +0 -44
- package/lib/module/index.js.map +0 -1
- package/lib/typescript/commonjs/package.json +0 -1
- package/lib/typescript/commonjs/src/index.d.ts +0 -13
- package/lib/typescript/commonjs/src/index.d.ts.map +0 -1
- package/lib/typescript/module/package.json +0 -1
- package/lib/typescript/module/src/index.d.ts +0 -13
- package/lib/typescript/module/src/index.d.ts.map +0 -1
package/README.md
CHANGED
|
@@ -10,15 +10,57 @@ npm install react-native-calculator-keyboard
|
|
|
10
10
|
|
|
11
11
|
## Usage
|
|
12
12
|
|
|
13
|
-
|
|
14
13
|
```js
|
|
15
|
-
import
|
|
14
|
+
import InputCalculator from '@momo-kits/calculator-keyboard';
|
|
16
15
|
|
|
17
16
|
// ...
|
|
18
17
|
|
|
19
|
-
<
|
|
18
|
+
<InputCalculator
|
|
19
|
+
mode="NumDefault"
|
|
20
|
+
customKeyText="Next"
|
|
21
|
+
onCustomKeyEvent={() => console.log('Custom key pressed')}
|
|
22
|
+
/>;
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
## Requirements
|
|
26
|
+
|
|
27
|
+
**React Native 0.80+** with **Fabric (New Architecture) enabled**.
|
|
28
|
+
|
|
29
|
+
This library is **pure Fabric** implementation with:
|
|
30
|
+
|
|
31
|
+
- ✅ Zero RCTBridge dependencies
|
|
32
|
+
- ✅ Native C++ ComponentView on iOS
|
|
33
|
+
- ✅ Fabric ViewManager with codegen delegates on Android
|
|
34
|
+
- ✅ All Props, Events, Commands auto-generated by codegen
|
|
35
|
+
- ❌ No Paper (old architecture) support
|
|
36
|
+
|
|
37
|
+
### Android Setup
|
|
38
|
+
|
|
39
|
+
Add to your `gradle.properties`:
|
|
40
|
+
|
|
41
|
+
```properties
|
|
42
|
+
newArchEnabled=true
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
Then rebuild:
|
|
46
|
+
|
|
47
|
+
```bash
|
|
48
|
+
cd android && ./gradlew clean && cd ..
|
|
49
|
+
npx react-native run-android
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
### iOS Setup
|
|
53
|
+
|
|
54
|
+
**Required**: Set the environment variable before installing pods:
|
|
55
|
+
|
|
56
|
+
```bash
|
|
57
|
+
cd ios
|
|
58
|
+
RCT_NEW_ARCH_ENABLED=1 pod install
|
|
59
|
+
cd ..
|
|
60
|
+
npx react-native run-ios
|
|
20
61
|
```
|
|
21
62
|
|
|
63
|
+
**Note**: This library uses Fabric ComponentView (`.mm` files) and will not work without `RCT_NEW_ARCH_ENABLED=1`.
|
|
22
64
|
|
|
23
65
|
## Contributing
|
|
24
66
|
|
|
@@ -1,61 +1,80 @@
|
|
|
1
1
|
package com.calculatorkeyboard
|
|
2
2
|
|
|
3
3
|
import android.annotation.SuppressLint
|
|
4
|
+
import android.content.Context
|
|
4
5
|
import android.content.res.ColorStateList
|
|
5
6
|
import android.graphics.Color
|
|
6
7
|
import android.graphics.drawable.GradientDrawable
|
|
7
8
|
import android.view.Gravity
|
|
8
9
|
import android.widget.Button
|
|
9
10
|
import android.widget.ImageButton
|
|
10
|
-
import androidx.appcompat.app.AppCompatActivity
|
|
11
11
|
import androidx.constraintlayout.widget.ConstraintLayout
|
|
12
|
-
import androidx.core.graphics.ColorUtils
|
|
13
12
|
import com.facebook.react.uimanager.ThemedReactContext
|
|
14
13
|
import org.mariuszgromada.math.mxparser.Expression
|
|
15
14
|
import androidx.core.graphics.toColorInt
|
|
16
|
-
import com.
|
|
17
|
-
|
|
15
|
+
import com.calculatorkeyboard.InputCalculatorViewManager.Companion.calculatorHeight
|
|
16
|
+
import com.facebook.react.bridge.Arguments
|
|
17
|
+
import com.facebook.react.bridge.ReactContext
|
|
18
|
+
import com.facebook.react.uimanager.UIManagerHelper
|
|
19
|
+
import com.facebook.react.uimanager.events.RCTEventEmitter
|
|
18
20
|
|
|
19
21
|
@SuppressLint("SetTextI18n", "ViewConstructor")
|
|
20
22
|
class CustomKeyboardView(
|
|
21
23
|
context: ThemedReactContext,
|
|
22
24
|
private val editText: CalculatorEditText
|
|
23
25
|
) : ConstraintLayout(context) {
|
|
24
|
-
private val
|
|
25
|
-
listOf("
|
|
26
|
-
listOf("
|
|
27
|
-
listOf("
|
|
28
|
-
listOf("
|
|
29
|
-
|
|
26
|
+
private val numWithCTAKeys = listOf(
|
|
27
|
+
listOf("1", "2", "3", "÷", "back"),
|
|
28
|
+
listOf("4", "5", "6", "×", "="),
|
|
29
|
+
listOf("7", "8", "9", "-", "Tiếp"),
|
|
30
|
+
listOf("000", "0", "+")
|
|
31
|
+
)
|
|
32
|
+
private val defaultKeys = listOf(
|
|
33
|
+
listOf("1", "2", "3", "÷", "AC"),
|
|
34
|
+
listOf("4", "5", "6", "×", "back"),
|
|
35
|
+
listOf("7", "8", "9", "-", "="),
|
|
36
|
+
listOf("000", "0", "+")
|
|
30
37
|
)
|
|
31
|
-
private val specialKeys = listOf("=", "-", "×", "÷", "
|
|
38
|
+
private val specialKeys = listOf("=", "-", "×", "÷", "back", "+", "AC")
|
|
32
39
|
private val separatorWidth = 8f
|
|
33
40
|
private var specialButtonColor: Int = "#D8D8D8".toColorInt()
|
|
34
41
|
|
|
35
|
-
|
|
36
|
-
val activity = context.currentActivity as? AppCompatActivity
|
|
37
|
-
if (activity != null) {
|
|
38
|
-
val displayMetrics = resources.displayMetrics
|
|
39
|
-
val widthButton = (displayMetrics.widthPixels - separatorWidth * 2 - 3 * separatorWidth) / 4f
|
|
40
|
-
val heightButton = (290.dpToPx() - separatorWidth * 2 - 4 * separatorWidth) / 5
|
|
42
|
+
private var keyboardMode: String = "NumDefault"
|
|
41
43
|
|
|
42
|
-
|
|
43
|
-
|
|
44
|
+
private var customKeyButton: Button? = null
|
|
45
|
+
private var customKeyButtonBackground: Int = "#D8D8D8".toColorInt()
|
|
46
|
+
private var customKeyButtonTextColor: Int = Color.BLACK
|
|
47
|
+
private var customKeyButtonState: String = "default"
|
|
44
48
|
|
|
49
|
+
init {
|
|
50
|
+
isClickable = false
|
|
51
|
+
isFocusable = false
|
|
52
|
+
isFocusableInTouchMode = false
|
|
53
|
+
clipToPadding = false
|
|
54
|
+
clipChildren = false
|
|
45
55
|
}
|
|
46
56
|
|
|
47
|
-
private fun renderUI(
|
|
57
|
+
private fun renderUI() {
|
|
58
|
+
val displayMetrics = resources.displayMetrics
|
|
59
|
+
val buttonWidth = (displayMetrics.widthPixels - separatorWidth * 2 - 4 * separatorWidth) / 5f
|
|
60
|
+
val buttonHeight = (calculatorHeight - separatorWidth * 2 - 3 * separatorWidth) / 4
|
|
61
|
+
|
|
48
62
|
var yOffset = separatorWidth
|
|
49
|
-
|
|
63
|
+
val keys = if (keyboardMode == "NumWithCTA") numWithCTAKeys else defaultKeys
|
|
64
|
+
for ((rowIndex, row) in keys.withIndex()) {
|
|
50
65
|
var xOffset = separatorWidth
|
|
51
|
-
for ((
|
|
66
|
+
for ((colIndex, key) in row.withIndex()) {
|
|
67
|
+
val isMainKey = rowIndex == 2 && colIndex == 4
|
|
68
|
+
val isMainCTAKey = isMainKey && keyboardMode == "NumWithCTA"
|
|
52
69
|
val width = if (key == "000") buttonWidth * 2 + separatorWidth else buttonWidth
|
|
53
|
-
val height = if (
|
|
70
|
+
val height = if (isMainKey) buttonHeight * 2 + separatorWidth else buttonHeight
|
|
54
71
|
|
|
55
72
|
val button = if (key == "back") {
|
|
56
73
|
createImageButton(key, xOffset, yOffset, buttonWidth.toInt(), buttonHeight.toInt())
|
|
57
74
|
} else {
|
|
58
|
-
createButton(key, xOffset, yOffset, width.toInt(), height.toInt())
|
|
75
|
+
createButton(key, xOffset, yOffset, width.toInt(), height.toInt(), isMainKey, isMainCTAKey).also { b ->
|
|
76
|
+
if (isMainCTAKey) customKeyButton = b
|
|
77
|
+
}
|
|
59
78
|
}
|
|
60
79
|
|
|
61
80
|
addView(button)
|
|
@@ -72,8 +91,9 @@ class CustomKeyboardView(
|
|
|
72
91
|
yOffset: Float,
|
|
73
92
|
buttonWidth: Int,
|
|
74
93
|
buttonHeight: Int,
|
|
94
|
+
isMainKey: Boolean,
|
|
95
|
+
isMainCTAKey: Boolean
|
|
75
96
|
): Button {
|
|
76
|
-
val specialKeys = listOf("=", "-", "×", "÷", "AC", "back", "+")
|
|
77
97
|
return Button(context).apply {
|
|
78
98
|
val shapeInit = GradientDrawable().apply {
|
|
79
99
|
shape = GradientDrawable.RECTANGLE
|
|
@@ -85,9 +105,11 @@ class CustomKeyboardView(
|
|
|
85
105
|
background = shapeInit
|
|
86
106
|
text = key
|
|
87
107
|
setTypeface(typeface)
|
|
88
|
-
textSize = 24.toFloat()
|
|
108
|
+
textSize = (if (isMainCTAKey) 18 else 24).toFloat()
|
|
89
109
|
setTextColor(Color.BLACK)
|
|
90
110
|
stateListAnimator = null
|
|
111
|
+
maxLines = 1
|
|
112
|
+
isAllCaps = false
|
|
91
113
|
layoutParams = LayoutParams(
|
|
92
114
|
buttonWidth,
|
|
93
115
|
buttonHeight
|
|
@@ -95,7 +117,7 @@ class CustomKeyboardView(
|
|
|
95
117
|
constrainedWidth = false
|
|
96
118
|
}
|
|
97
119
|
|
|
98
|
-
if (specialKeys.contains(key)) {
|
|
120
|
+
if (specialKeys.contains(key) || isMainKey) {
|
|
99
121
|
background = GradientDrawable().apply {
|
|
100
122
|
shape = GradientDrawable.RECTANGLE
|
|
101
123
|
cornerRadius = 24f
|
|
@@ -104,10 +126,13 @@ class CustomKeyboardView(
|
|
|
104
126
|
setTextColor(Color.BLACK)
|
|
105
127
|
}
|
|
106
128
|
|
|
129
|
+
isClickable = true
|
|
130
|
+
isFocusable = false
|
|
131
|
+
isFocusableInTouchMode = false
|
|
107
132
|
|
|
108
133
|
translationX = xOffset.toInt().toFloat()
|
|
109
134
|
translationY = yOffset.toInt().toFloat()
|
|
110
|
-
setOnClickListener { onKeyPress(key) }
|
|
135
|
+
setOnClickListener { onKeyPress(key, isMainCTAKey) }
|
|
111
136
|
}
|
|
112
137
|
}
|
|
113
138
|
|
|
@@ -132,66 +157,35 @@ class CustomKeyboardView(
|
|
|
132
157
|
).apply {
|
|
133
158
|
constrainedWidth = false
|
|
134
159
|
}
|
|
160
|
+
|
|
161
|
+
isClickable = true
|
|
162
|
+
isFocusable = false
|
|
163
|
+
isFocusableInTouchMode = false
|
|
164
|
+
|
|
135
165
|
translationX = xOffset
|
|
136
166
|
translationY = yOffset
|
|
137
167
|
setImageResource(android.R.drawable.ic_input_delete)
|
|
138
168
|
setImageTintList(ColorStateList.valueOf(Color.BLACK))
|
|
139
|
-
setOnClickListener { onKeyPress(key) }
|
|
169
|
+
setOnClickListener { onKeyPress(key, false) }
|
|
140
170
|
}
|
|
141
171
|
}
|
|
142
172
|
|
|
143
|
-
fun
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
if (child is Button) {
|
|
148
|
-
val key = child.text.toString()
|
|
149
|
-
if (specialKeys.contains(key)) {
|
|
150
|
-
if (key == "=") {
|
|
151
|
-
child.background = GradientDrawable().apply {
|
|
152
|
-
shape = GradientDrawable.RECTANGLE
|
|
153
|
-
cornerRadius = 24f
|
|
154
|
-
setColor(specialButtonColor)
|
|
155
|
-
}
|
|
156
|
-
} else {
|
|
157
|
-
child.background = GradientDrawable().apply {
|
|
158
|
-
shape = GradientDrawable.RECTANGLE
|
|
159
|
-
cornerRadius = 24f
|
|
160
|
-
setColor(specialButtonColor)
|
|
161
|
-
}
|
|
162
|
-
}
|
|
163
|
-
child.setTextColor(Color.BLACK)
|
|
164
|
-
}
|
|
165
|
-
} else if (child is ImageButton) {
|
|
166
|
-
child.background = GradientDrawable().apply {
|
|
167
|
-
shape = GradientDrawable.RECTANGLE
|
|
168
|
-
cornerRadius = 24f
|
|
169
|
-
setColor(specialButtonColor)
|
|
170
|
-
}
|
|
171
|
-
}
|
|
173
|
+
private fun onKeyPress(key: String, isMainCTAKey: Boolean) {
|
|
174
|
+
if (isMainCTAKey) {
|
|
175
|
+
emitCustomKey(context)
|
|
176
|
+
return
|
|
172
177
|
}
|
|
173
|
-
}
|
|
174
178
|
|
|
175
|
-
|
|
179
|
+
emitKeyPress(context, key)
|
|
176
180
|
when (key) {
|
|
177
|
-
"AC" ->
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
"back" -> {
|
|
182
|
-
onBackSpace()
|
|
183
|
-
}
|
|
184
|
-
|
|
185
|
-
"=" -> {
|
|
186
|
-
calculateResult()
|
|
187
|
-
}
|
|
188
|
-
|
|
181
|
+
"AC" -> clearText()
|
|
182
|
+
"back" -> onBackSpace()
|
|
183
|
+
"=" -> calculateResult()
|
|
189
184
|
"×", "+", "-", "÷" -> keyDidPress(" $key ")
|
|
190
|
-
|
|
191
|
-
else -> {
|
|
192
|
-
editText.text?.insert(editText.selectionStart, key)
|
|
193
|
-
}
|
|
185
|
+
else -> editText.text?.insert(editText.selectionStart, key)
|
|
194
186
|
}
|
|
187
|
+
|
|
188
|
+
reformatAndKeepSelection(editText)
|
|
195
189
|
}
|
|
196
190
|
|
|
197
191
|
private fun keyDidPress(key: String) {
|
|
@@ -204,26 +198,39 @@ class CustomKeyboardView(
|
|
|
204
198
|
}
|
|
205
199
|
|
|
206
200
|
private fun onBackSpace() {
|
|
207
|
-
val
|
|
208
|
-
val
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
201
|
+
val formatted = editText.text?.toString().orEmpty()
|
|
202
|
+
val caretFmt = editText.selectionStart.coerceAtLeast(0)
|
|
203
|
+
|
|
204
|
+
val rawBefore = stripGroupDots(formatted)
|
|
205
|
+
val caretRaw = formattedCaretToRaw(formatted, caretFmt)
|
|
206
|
+
|
|
207
|
+
if (caretRaw <= 0) return
|
|
208
|
+
|
|
209
|
+
val rawAfter = buildString(rawBefore.length - 1) {
|
|
210
|
+
append(rawBefore, 0, caretRaw - 1)
|
|
211
|
+
append(rawBefore, caretRaw, rawBefore.length)
|
|
213
212
|
}
|
|
213
|
+
|
|
214
|
+
val formattedAfter = formatNumberGroups(rawAfter)
|
|
215
|
+
editText.setText(formattedAfter)
|
|
216
|
+
val newCaretFmt = rawCaretToFormatted(caretRaw - 1, formattedAfter)
|
|
217
|
+
editText.setSelection(newCaretFmt.coerceIn(0, formattedAfter.length))
|
|
214
218
|
}
|
|
215
219
|
|
|
220
|
+
|
|
216
221
|
private fun calculateResult() {
|
|
217
|
-
val
|
|
222
|
+
val raw = editText.text?.toString().orEmpty()
|
|
223
|
+
val normalized = raw.replace(".", "")
|
|
224
|
+
.replace("×", "*")
|
|
225
|
+
.replace("÷", "/")
|
|
226
|
+
|
|
218
227
|
val pattern = "^\\s*(-?\\d+(\\.\\d+)?\\s*[-+*/]\\s*)*-?\\d+(\\.\\d+)?\\s*$"
|
|
219
|
-
|
|
220
|
-
if (regex.matches(text)) {
|
|
228
|
+
if (Regex(pattern).matches(normalized)) {
|
|
221
229
|
try {
|
|
222
|
-
val result = eval(
|
|
223
|
-
editText.setTextKeepState(result)
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
}
|
|
230
|
+
val result = eval(normalized)?.toString() ?: return
|
|
231
|
+
editText.setTextKeepState(formatNumberGroups(result))
|
|
232
|
+
editText.setSelection(editText.text?.length ?: 0)
|
|
233
|
+
} catch (_: Exception) { /* ignore */ }
|
|
227
234
|
} else {
|
|
228
235
|
println("Invalid expression")
|
|
229
236
|
}
|
|
@@ -239,4 +246,132 @@ class CustomKeyboardView(
|
|
|
239
246
|
}
|
|
240
247
|
}
|
|
241
248
|
|
|
249
|
+
private fun emitKeyPress(context: Context, key: String) {
|
|
250
|
+
val reactContext = context as ReactContext
|
|
251
|
+
val surfaceId = UIManagerHelper.getSurfaceId(reactContext)
|
|
252
|
+
val eventDispatcher = UIManagerHelper.getEventDispatcherForReactTag(reactContext, editText.id)
|
|
253
|
+
val payload =
|
|
254
|
+
Arguments.createMap().apply {
|
|
255
|
+
putString("key", key)
|
|
256
|
+
}
|
|
257
|
+
val event = OnKeyPressEvent(surfaceId, editText.id, payload)
|
|
258
|
+
|
|
259
|
+
eventDispatcher?.dispatchEvent(event)
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
private fun emitCustomKey(context: Context) {
|
|
263
|
+
val reactContext = context as ReactContext
|
|
264
|
+
val surfaceId = UIManagerHelper.getSurfaceId(reactContext)
|
|
265
|
+
val eventDispatcher = UIManagerHelper.getEventDispatcherForReactTag(reactContext, editText.id)
|
|
266
|
+
|
|
267
|
+
val event = OnCustomKeyPressEvent(surfaceId, editText.id)
|
|
268
|
+
|
|
269
|
+
eventDispatcher?.dispatchEvent(event)
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
fun setCustomKeyText(text: String) {
|
|
273
|
+
customKeyButton?.text = text
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
fun setMode(mode: String) {
|
|
277
|
+
keyboardMode = mode
|
|
278
|
+
renderUI()
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
fun setCustomKeyBackground(background: Int) {
|
|
282
|
+
customKeyButtonBackground = background
|
|
283
|
+
updateCustomKeyUI(background, customKeyButtonTextColor)
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
fun setCustomKeyTextColor(textColor: Int) {
|
|
287
|
+
customKeyButtonTextColor = textColor
|
|
288
|
+
updateCustomKeyUI(customKeyButtonBackground, textColor)
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
|
|
292
|
+
fun setCustomKeyState(state: String) {
|
|
293
|
+
customKeyButtonState = state
|
|
294
|
+
customKeyButton?.isEnabled = state != "disable"
|
|
295
|
+
updateCustomKeyUI(customKeyButtonBackground, customKeyButtonTextColor)
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
private fun updateCustomKeyUI(background: Int, textColor: Int){
|
|
299
|
+
customKeyButton?.background = GradientDrawable().apply {
|
|
300
|
+
shape = GradientDrawable.RECTANGLE
|
|
301
|
+
cornerRadius = 24f
|
|
302
|
+
setColor(background)
|
|
303
|
+
}
|
|
304
|
+
customKeyButton?.setTextColor(textColor)
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
private fun reformatAndKeepSelection(editText: CalculatorEditText) {
|
|
308
|
+
val formattedBefore = editText.text?.toString() ?: return
|
|
309
|
+
val caretFmtBefore = editText.selectionStart.coerceAtLeast(0)
|
|
310
|
+
|
|
311
|
+
val caretRaw = formattedCaretToRaw(formattedBefore, caretFmtBefore)
|
|
312
|
+
|
|
313
|
+
val formattedAfter = formatNumberGroups(formattedBefore)
|
|
314
|
+
|
|
315
|
+
if (formattedAfter != formattedBefore) {
|
|
316
|
+
editText.setText(formattedAfter)
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
val caretFmtAfter = rawCaretToFormatted(caretRaw, formattedAfter)
|
|
320
|
+
editText.setSelection(caretFmtAfter.coerceIn(0, formattedAfter.length))
|
|
321
|
+
}
|
|
322
|
+
private fun stripGroupDots(input: String): String {
|
|
323
|
+
val out = StringBuilder(input.length)
|
|
324
|
+
for (i in input.indices) {
|
|
325
|
+
val c = input[i]
|
|
326
|
+
if (c == '.' && isGroupDotAt(input, i)) {
|
|
327
|
+
} else {
|
|
328
|
+
out.append(c)
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
return out.toString()
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
private fun formatNumberGroups(input: String): String {
|
|
335
|
+
val noSep = stripGroupDots(input)
|
|
336
|
+
return Regex("\\d+").replace(noSep) { m ->
|
|
337
|
+
val s = m.value
|
|
338
|
+
val rev = s.reversed().chunked(3).joinToString(".")
|
|
339
|
+
rev.reversed()
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
private fun isGroupDotAt(s: String, i: Int): Boolean {
|
|
344
|
+
if (i < 0 || i >= s.length) return false
|
|
345
|
+
if (s[i] != '.') return false
|
|
346
|
+
val leftIsDigit = i - 1 >= 0 && s[i - 1].isDigit()
|
|
347
|
+
val rightIsDigit = i + 1 < s.length && s[i + 1].isDigit()
|
|
348
|
+
return leftIsDigit && rightIsDigit
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
private fun formattedCaretToRaw(formatted: String, caretFmt: Int): Int {
|
|
352
|
+
var rawIdx = 0
|
|
353
|
+
var i = 0
|
|
354
|
+
while (i < caretFmt && i < formatted.length) {
|
|
355
|
+
val c = formatted[i]
|
|
356
|
+
if (!(c == '.' && isGroupDotAt(formatted, i))) {
|
|
357
|
+
rawIdx++
|
|
358
|
+
}
|
|
359
|
+
i++
|
|
360
|
+
}
|
|
361
|
+
return rawIdx
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
private fun rawCaretToFormatted(rawCaret: Int, formatted: String): Int {
|
|
365
|
+
var rawSeen = 0
|
|
366
|
+
var i = 0
|
|
367
|
+
while (i < formatted.length) {
|
|
368
|
+
val c = formatted[i]
|
|
369
|
+
if (!(c == '.' && isGroupDotAt(formatted, i))) {
|
|
370
|
+
if (rawSeen == rawCaret) return i
|
|
371
|
+
rawSeen++
|
|
372
|
+
}
|
|
373
|
+
i++
|
|
374
|
+
}
|
|
375
|
+
return formatted.length
|
|
376
|
+
}
|
|
242
377
|
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
package com.calculatorkeyboard
|
|
2
|
+
|
|
3
|
+
import com.facebook.react.bridge.WritableMap
|
|
4
|
+
import com.facebook.react.uimanager.events.Event
|
|
5
|
+
|
|
6
|
+
class OnChangeEvent(
|
|
7
|
+
surfaceId: Int,
|
|
8
|
+
viewId: Int,
|
|
9
|
+
private val payload: WritableMap
|
|
10
|
+
) : Event<OnChangeEvent>(surfaceId, viewId) {
|
|
11
|
+
override fun getEventName() = "onChange"
|
|
12
|
+
|
|
13
|
+
override fun getEventData() = payload
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
class OnKeyPressEvent(
|
|
17
|
+
surfaceId: Int,
|
|
18
|
+
viewId: Int,
|
|
19
|
+
private val payload: WritableMap
|
|
20
|
+
) : Event<OnChangeEvent>(surfaceId, viewId) {
|
|
21
|
+
override fun getEventName() = "onKeyPress"
|
|
22
|
+
|
|
23
|
+
override fun getEventData() = payload
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
class OnCustomKeyPressEvent(
|
|
27
|
+
surfaceId: Int,
|
|
28
|
+
viewId: Int
|
|
29
|
+
) : Event<OnChangeEvent>(surfaceId, viewId) {
|
|
30
|
+
override fun getEventName() = "onCustomKeyEvent"
|
|
31
|
+
|
|
32
|
+
override fun getEventData() = null
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
|