@momo-kits/calculator-keyboard 0.150.1-rn80.8 → 0.150.2-beta-sp.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.
@@ -1,46 +1,71 @@
1
1
  package com.calculatorkeyboard
2
2
 
3
3
  import android.annotation.SuppressLint
4
- import android.app.Activity
5
- import android.graphics.PixelFormat
6
- import android.graphics.drawable.ColorDrawable
7
- import android.view.Gravity
4
+ import android.content.Context
5
+ import android.graphics.Color
8
6
  import android.view.KeyEvent
9
- import android.view.View
10
- import android.view.WindowInsets
11
- import android.view.WindowManager
12
- import android.widget.PopupWindow
13
- import androidx.constraintlayout.widget.ConstraintLayout
7
+ import android.view.inputmethod.InputMethodManager
14
8
  import androidx.core.graphics.toColorInt
9
+ import androidx.core.view.ViewCompat
10
+ import androidx.core.view.WindowInsetsCompat
15
11
  import com.facebook.react.bridge.ReadableArray
16
12
  import com.facebook.react.bridge.UiThreadUtil
13
+ import com.facebook.react.uimanager.PixelUtil.dpToPx
17
14
  import com.facebook.react.uimanager.ThemedReactContext
18
15
  import com.facebook.react.uimanager.annotations.ReactProp
19
16
  import com.facebook.react.views.textinput.ReactEditText
20
17
  import com.facebook.react.views.textinput.ReactTextInputManager
21
- import androidx.core.graphics.drawable.toDrawable
22
- import androidx.core.view.ViewCompat
23
- import androidx.core.view.WindowInsetsCompat
24
- import com.facebook.react.uimanager.PixelUtil.dpToPx
25
18
 
26
19
  class RCTInputCalculator : ReactTextInputManager() {
27
20
 
28
21
  private var keyboardView: CustomKeyboardView? = null
29
- private var calculatorHeight: Int = 290.dpToPx().toInt()
30
- private var popup: PopupWindow? = null
31
- private val animationDuration = 250L
32
-
33
22
  private lateinit var editText: CalculatorEditText
23
+ private lateinit var imm: InputMethodManager
24
+
25
+ private val overlayHost = KeyboardOverlayHost()
26
+
27
+ private var backListenerAttached = false
28
+ private var overlayShowing = false
34
29
 
35
30
  override fun getName() = REACT_CLASS
36
31
 
37
32
  companion object {
38
33
  const val REACT_CLASS = "RCTInputCalculator"
34
+ val calculatorHeight: Int = 240.dpToPx().toInt()
39
35
  }
40
36
 
41
37
  @ReactProp(name = "value")
42
38
  fun setValue(view: ReactEditText, value: String?) {
43
- // view.setText(value)
39
+ UiThreadUtil.runOnUiThread {
40
+ val e = view.editableText ?: run { view.setText(value ?: ""); return@runOnUiThread }
41
+ val newText = value ?: ""
42
+ if (e.toString() == newText) return@runOnUiThread
43
+
44
+ val wasFocused = view.hasFocus()
45
+ val atEnd = wasFocused && view.selectionStart == e.length && view.selectionEnd == e.length
46
+ e.replace(0, e.length, newText)
47
+ if (!wasFocused || atEnd) view.setSelection(newText.length)
48
+ }
49
+ }
50
+
51
+ @ReactProp(name = "customKeyText")
52
+ fun setCustomKeyText(view: ReactEditText, text: String?) {
53
+ keyboardView?.setCustomKeyText(text ?: "Xong")
54
+ }
55
+
56
+ @ReactProp(name = "customKeyBackground")
57
+ fun setCustomKeyBackground(view: ReactEditText, background: String?) {
58
+ keyboardView?.setCustomKeyBackground((background ?: "#d8d8d8").toColorInt())
59
+ }
60
+
61
+ @ReactProp(name = "customKeyTextColor")
62
+ fun setCustomKeyTextColor(view: ReactEditText, textColor: String?) {
63
+ keyboardView?.setCustomKeyTextColor(textColor?.toColorInt() ?: Color.BLACK)
64
+ }
65
+
66
+ @ReactProp(name = "customKeyState")
67
+ fun setCustomKeyState(view: ReactEditText, state: String?) {
68
+ keyboardView?.setCustomKeyState(state ?: "default")
44
69
  }
45
70
 
46
71
  @ReactProp(name = "keyboardColor")
@@ -50,8 +75,32 @@ class RCTInputCalculator : ReactTextInputManager() {
50
75
 
51
76
  @SuppressLint("ClickableViewAccessibility")
52
77
  override fun createViewInstance(context: ThemedReactContext): ReactEditText {
78
+ imm = context.getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager
79
+
53
80
  editText = CalculatorEditText(context).apply {
54
81
  showSoftInputOnFocus = false
82
+ isFocusableInTouchMode = true
83
+ setOnTouchListener { v, event ->
84
+ if (event.action == android.view.MotionEvent.ACTION_DOWN) {
85
+ showSoftInputOnFocus = false
86
+ ViewCompat.getWindowInsetsController(this)?.hide(WindowInsetsCompat.Type.ime())
87
+ imm.hideSoftInputFromWindow(windowToken, 0)
88
+
89
+ if (!isFocused) requestFocus()
90
+
91
+ val kb = keyboardView
92
+ if (kb != null && !overlayShowing) {
93
+ overlayHost.show(
94
+ anchorView = this,
95
+ keyboardView = kb,
96
+ heightPx = calculatorHeight
97
+ )
98
+ overlayShowing = true
99
+ }
100
+ return@setOnTouchListener true
101
+ }
102
+ false
103
+ }
55
104
  }
56
105
 
57
106
  keyboardView = CustomKeyboardView(context, editText).apply {
@@ -59,19 +108,34 @@ class RCTInputCalculator : ReactTextInputManager() {
59
108
  elevation = 24f
60
109
  }
61
110
 
111
+ if (!backListenerAttached) {
112
+ editText.setOnKeyListener { v, keyCode, event ->
113
+ if (keyCode == KeyEvent.KEYCODE_BACK && event.action == KeyEvent.ACTION_UP && v.hasFocus()) {
114
+ v.clearFocus()
115
+ true
116
+ } else false
117
+ }
118
+ backListenerAttached = true
119
+ }
120
+
62
121
  editText.onFocusListener = object : CalculatorEditText.OnFocusChangeListener {
63
122
  override fun onFocusChange(view: CalculatorEditText, hasFocus: Boolean) {
64
123
  UiThreadUtil.runOnUiThread {
65
124
  if (hasFocus) {
66
- showKeyboardPopup(view)
125
+ disableSystemImeFor(view)
126
+ if (!overlayShowing) {
127
+ val kb = keyboardView ?: return@runOnUiThread
128
+ overlayHost.show(
129
+ anchorView = view,
130
+ keyboardView = kb,
131
+ heightPx = calculatorHeight
132
+ )
133
+ overlayShowing = true
134
+ }
67
135
  } else {
68
- hideKeyboardPopup()
69
- }
70
- view.setOnKeyListener { v, keyCode, _ ->
71
- if (keyCode == KeyEvent.KEYCODE_BACK && hasFocus) {
72
- v.clearFocus()
73
- true
74
- } else false
136
+ overlayHost.hide()
137
+ overlayShowing = false
138
+ enableSystemImeFor(view)
75
139
  }
76
140
  }
77
141
  }
@@ -80,87 +144,54 @@ class RCTInputCalculator : ReactTextInputManager() {
80
144
  return editText
81
145
  }
82
146
 
83
- override fun getCommandsMap(): Map<String, Int> {
84
- return mapOf(
85
- "blur" to 1,
86
- "focus" to 2
87
- )
88
- }
89
147
 
90
- override fun receiveCommand(reactEditText: ReactEditText, commandId: Int, args: ReadableArray?) {
91
- when (commandId) {
92
- 1 -> blur()
93
- 2 -> focus()
94
- }
148
+ override fun onDropViewInstance(view: ReactEditText) {
149
+ super.onDropViewInstance(view)
150
+ overlayHost.hide()
151
+ overlayHost.detach()
152
+ overlayShowing = false
153
+
95
154
  }
96
155
 
97
- private fun ensurePopup() {
98
- if (popup != null) return
99
- val content = keyboardView ?: return
100
-
101
- popup = PopupWindow(
102
- content,
103
- WindowManager.LayoutParams.MATCH_PARENT,
104
- calculatorHeight + bottomInsetFrom(editText.rootView),
105
- false
106
- ).apply {
107
- setBackgroundDrawable(android.graphics.Color.TRANSPARENT.toDrawable())
108
- isOutsideTouchable = false
109
- isClippingEnabled = false
110
- softInputMode = WindowManager.LayoutParams.SOFT_INPUT_ADJUST_NOTHING
111
- inputMethodMode = PopupWindow.INPUT_METHOD_NOT_NEEDED
156
+ private fun disableSystemImeFor(v: ReactEditText) {
157
+ v.showSoftInputOnFocus = false
158
+ if (android.os.Build.VERSION.SDK_INT >= 30) {
159
+ ViewCompat.getWindowInsetsController(v)?.hide(WindowInsetsCompat.Type.ime())
112
160
  }
161
+ imm.hideSoftInputFromWindow(v.windowToken, 0)
113
162
  }
114
163
 
115
- private fun bottomInsetFrom(view: View): Int {
116
- val insets = ViewCompat.getRootWindowInsets(view) ?: return 0
117
- val mask = WindowInsetsCompat.Type.navigationBars() or WindowInsetsCompat.Type.displayCutout()
118
- return insets.getInsets(mask).bottom
164
+ private fun enableSystemImeFor(v: ReactEditText) {
165
+ v.showSoftInputOnFocus = true
119
166
  }
120
167
 
121
- private fun showKeyboardPopup(anchor: View) {
122
- ensurePopup()
123
- val p = popup ?: return
124
- if (p.isShowing) return
168
+ override fun getCommandsMap(): Map<String, Int> = mapOf("blur" to 1, "focus" to 2)
125
169
 
126
- val root = keyboardView ?: return
127
-
128
- root.translationY = calculatorHeight.toFloat()
129
- p.showAtLocation(anchor.rootView, Gravity.BOTTOM or Gravity.START, 0, 0)
130
-
131
- root.animate()
132
- ?.translationY(0f)
133
- ?.setDuration(animationDuration)
134
- ?.start()
170
+ override fun receiveCommand(reactEditText: ReactEditText, commandId: Int, args: ReadableArray?) {
171
+ when (commandId) {
172
+ 1 -> blur()
173
+ 2 -> focus()
174
+ }
135
175
  }
136
176
 
137
- private fun hideKeyboardPopup() {
138
- val p = popup ?: return
139
- if (!p.isShowing) return
140
- val root = keyboardView ?: return
141
-
142
- root.animate()
143
- .translationY(calculatorHeight.toFloat())
144
- .setDuration(animationDuration)
145
- .withEndAction {
146
- try {
147
- if (p.isShowing) p.dismiss()
148
- } catch (_: Throwable) { }
149
- }
150
- .start()
177
+ override fun getExportedCustomBubblingEventTypeConstants(): MutableMap<String, Any> {
178
+ val base = super.getExportedCustomBubblingEventTypeConstants().toMutableMap()
179
+ base["onKeyPress"] = mapOf("phasedRegistrationNames" to mapOf("bubbled" to "onKeyPress"))
180
+ return base
181
+ }
151
182
 
152
- popup = null
183
+ override fun getExportedCustomDirectEventTypeConstants(): MutableMap<String, Any> {
184
+ val base = super.getExportedCustomDirectEventTypeConstants().toMutableMap()
185
+ base["onCustomKeyEvent"] = mapOf("registrationName" to "onCustomKeyEvent")
186
+ return base
153
187
  }
154
188
 
155
189
  private fun focus() {
156
- UiThreadUtil.runOnUiThread {
157
- if (!editText.isFocused) editText.requestFocus()
158
- }
190
+ UiThreadUtil.runOnUiThread { if (!editText.isFocused) editText.requestFocus() }
159
191
  }
160
192
 
161
193
  private fun blur() {
162
- UiThreadUtil.runOnUiThread {
163
- if (editText.isFocused) editText.clearFocus()
164
- }
194
+ UiThreadUtil.runOnUiThread { if (editText.isFocused) editText.clearFocus() }
165
195
  }
166
196
  }
197
+
@@ -8,14 +8,20 @@ class CalculatorKeyboardView: UIView {
8
8
  weak var input: InputCalculator?
9
9
 
10
10
  private let keys: [[String]] = [
11
- ["AC", "÷", "×", "back"],
12
- ["7", "8", "9", "-"],
13
- ["4", "5", "6", "+"],
14
- ["1", "2", "3", "="],
15
- ["000", "0"]
11
+ ["1", "2", "3", "÷", "back"],
12
+ ["4", "5", "6", "×", "="],
13
+ ["7", "8", "9", "-", "Xong"],
14
+ ["000", "0", "+"],
16
15
  ]
17
16
  private let SEPARATOR_WIDTH: CGFloat = 4
18
- private let specialKeys: Set<String> = ["=", "-", "×", "÷", "AC", "back", "+"]
17
+ private let specialKeys: Set<String> = ["=", "-", "×", "÷", "back", "+"]
18
+
19
+ var customKeyText: String? { didSet { updateCustomKeyTitle() } }
20
+ var customKeyBackground: String? { didSet { updateCustomKeyBackground() } }
21
+ var customKeyTextColor: String? { didSet { updateCustomKeyBackground() } }
22
+ var customKeyState: String? { didSet { updateCustomKeyBackground() } }
23
+ private weak var customKeyButton: UIButton?
24
+
19
25
 
20
26
  override init(frame: CGRect) {
21
27
  super.init(frame: frame)
@@ -34,8 +40,8 @@ class CalculatorKeyboardView: UIView {
34
40
  self.subviews.forEach { $0.removeFromSuperview() }
35
41
 
36
42
  backgroundColor = UIColor(hex: "#f2f2f6")
37
- let buttonWidth = (UIScreen.main.bounds.width - SEPARATOR_WIDTH * 2 - 3 * SEPARATOR_WIDTH) / 4
38
- let buttonHeight: CGFloat = (290 - SEPARATOR_WIDTH * 2 - 4 * SEPARATOR_WIDTH) / 5
43
+ let buttonWidth = (UIScreen.main.bounds.width - SEPARATOR_WIDTH * 2 - 4 * SEPARATOR_WIDTH) / 5
44
+ let buttonHeight: CGFloat = (240 - SEPARATOR_WIDTH * 2 - 3 * SEPARATOR_WIDTH) / 4
39
45
 
40
46
  // Create a wrapper view
41
47
  let contentView = UIView()
@@ -52,19 +58,22 @@ class CalculatorKeyboardView: UIView {
52
58
 
53
59
  // Add buttons to the wrapper view
54
60
  var yOffset: CGFloat = 0
55
- for row in keys {
61
+ for (rowIndex, row) in keys.enumerated() {
56
62
  var xOffset: CGFloat = 0
57
- for key in row {
63
+ for (colIndex, key) in row.enumerated() {
64
+ let isCustomKey = colIndex == 4 && rowIndex == 2
58
65
  let button = UIButton(type: .system)
59
66
  button.backgroundColor = UIColor.white
60
67
  button.layer.cornerRadius = 8
61
- button.setTitle(key, for: .normal)
68
+ let title = isCustomKey ? (customKeyText ?? key) : key
69
+ button.setTitle(title, for: .normal)
62
70
  button.setTitleColor(.black, for: .normal)
63
- button.titleLabel?.font = UIFont.systemFont(ofSize: 24, weight: .medium)
71
+ button.titleLabel?.font = UIFont.systemFont(ofSize: isCustomKey ? 18 : 24, weight: .medium)
64
72
  button.nativeID = key
73
+ button.tag = isCustomKey ? 1 : 0
65
74
 
66
75
  var buttonFrame = CGRect(x: xOffset, y: yOffset, width: buttonWidth, height: buttonHeight)
67
- if key == "=" {
76
+ if isCustomKey {
68
77
  buttonFrame.size.height = buttonHeight * 2 + SEPARATOR_WIDTH
69
78
  }
70
79
  if key == "000" {
@@ -85,6 +94,10 @@ class CalculatorKeyboardView: UIView {
85
94
  button.backgroundColor = color
86
95
  }
87
96
 
97
+ if isCustomKey {
98
+ self.customKeyButton = button
99
+ }
100
+
88
101
  button.addTarget(self, action: #selector(keyPressed(_:)), for: .touchUpInside)
89
102
  contentView.addSubview(button)
90
103
 
@@ -96,12 +109,37 @@ class CalculatorKeyboardView: UIView {
96
109
  }
97
110
  }
98
111
 
112
+ private func updateCustomKeyTitle() {
113
+ guard let btn = customKeyButton, let title = customKeyText else { return }
114
+ btn.setTitle(title, for: .normal)
115
+ btn.setImage(nil, for: .normal)
116
+ }
117
+
118
+ private func updateCustomKeyBackground() {
119
+ guard let btn = customKeyButton,
120
+ let background = customKeyBackground,
121
+ let textColor = customKeyTextColor,
122
+ let state = customKeyState else { return }
123
+
124
+
125
+
126
+ btn.isEnabled = state != "disable"
127
+ btn.backgroundColor = UIColor(hex: background)
128
+ btn.setTitleColor(UIColor(hex: textColor), for: .normal)
129
+
130
+ }
131
+
99
132
 
100
133
  @objc private func keyPressed(_ sender: UIButton) {
101
134
  guard let key = sender.nativeID else { return }
135
+ let isCustomKey = sender.tag == 1
136
+ if (isCustomKey) {
137
+ input?.emitCustomKey()
138
+ return
139
+ }
140
+
141
+ input?.emitKeyPress(key)
102
142
  switch key {
103
- case "AC":
104
- input?.clearText()
105
143
  case "back":
106
144
  input?.onBackSpace()
107
145
  case "=":
@@ -53,6 +53,11 @@ RCT_EXPORT_VIEW_PROPERTY(mostRecentEventCount, NSInteger)
53
53
  RCT_EXPORT_SHADOW_PROPERTY(text, NSString)
54
54
  RCT_EXPORT_SHADOW_PROPERTY(placeholder, NSString)
55
55
  RCT_EXPORT_SHADOW_PROPERTY(onContentSizeChange, RCTDirectEventBlock)
56
+ RCT_EXPORT_VIEW_PROPERTY(value, NSString)
57
+ RCT_EXPORT_VIEW_PROPERTY(onFocus, RCTBubblingEventBlock)
58
+ RCT_EXPORT_VIEW_PROPERTY(onBlur, RCTBubblingEventBlock)
59
+ RCT_EXPORT_VIEW_PROPERTY(onKeyPress, RCTBubblingEventBlock)
60
+ RCT_EXPORT_VIEW_PROPERTY(onCustomKeyEvent, RCTDirectEventBlock)
56
61
 
57
62
  RCT_EXPORT_METHOD(focus : (nonnull NSNumber *)viewTag)
58
63
  {
@@ -71,6 +76,10 @@ RCT_EXPORT_METHOD(blur : (nonnull NSNumber *)viewTag)
71
76
  }
72
77
 
73
78
  RCT_EXPORT_VIEW_PROPERTY(keyboardColor, UIColor)
79
+ RCT_EXPORT_VIEW_PROPERTY(customKeyText, NSString)
80
+ RCT_EXPORT_VIEW_PROPERTY(customKeyBackground, NSString)
81
+ RCT_EXPORT_VIEW_PROPERTY(customKeyTextColor, NSString)
82
+ RCT_EXPORT_VIEW_PROPERTY(customKeyState, NSString)
74
83
 
75
84
  @end
76
85
 
@@ -6,34 +6,87 @@ class RCTInputCalculator: RCTBaseTextInputViewManager {
6
6
  override func view() -> UIView! {
7
7
  return InputCalculator(bridge: bridge)
8
8
  }
9
-
9
+
10
10
  override static func requiresMainQueueSetup() -> Bool {
11
11
  return true
12
12
  }
13
-
13
+
14
14
  }
15
15
 
16
16
  class InputCalculator: RCTSinglelineTextInputView {
17
17
  var bridge: RCTBridge?
18
18
  var keyboardView: CalculatorKeyboardView?
19
+
20
+ @objc var onFocus: RCTBubblingEventBlock?
21
+ @objc var onBlur: RCTBubblingEventBlock?
22
+ @objc var onKeyPress: RCTBubblingEventBlock?
23
+ @objc var onCustomKeyEvent: RCTDirectEventBlock?
24
+
25
+ @objc var customKeyText: String? {
26
+ didSet { keyboardView?.customKeyText = customKeyText as String? }
27
+ }
19
28
 
20
- @objc var value: String = ""
29
+ @objc var customKeyBackground: String? {
30
+ didSet { keyboardView?.customKeyBackground = customKeyBackground }
31
+ }
21
32
 
33
+ @objc var customKeyTextColor: String? {
34
+ didSet { keyboardView?.customKeyTextColor = customKeyTextColor }
35
+ }
36
+
37
+ @objc var customKeyState: String? {
38
+ didSet { keyboardView?.customKeyState = customKeyState }
39
+ }
40
+
41
+ @objc func beginEditingInput(_ note: Notification) { onFocus?([:]) }
42
+ @objc func endEditingInput(_ note: Notification) { onBlur?([:]) }
43
+
44
+ @objc var value: String = "" {
45
+ didSet {
46
+ guard let tf = backedTextInputView as? UITextField else { return }
47
+ let old = tf.text ?? ""
48
+ if old == value { return }
49
+
50
+ let isFirstResponder = tf.isFirstResponder
51
+ let atEnd = isFirstResponder && tf.offset(from: tf.beginningOfDocument, to: tf.selectedTextRange?.start ?? tf.endOfDocument) == old.count
52
+ tf.text = value
53
+ if !isFirstResponder || atEnd,
54
+ let end = tf.position(from: tf.beginningOfDocument, offset: value.count) {
55
+ tf.selectedTextRange = tf.textRange(from: end, to: end)
56
+ }
57
+ }
58
+ }
59
+
22
60
  @objc var keyboardColor: UIColor = UIColor(hex: "#d9d9d9") {
23
61
  didSet {
24
62
  self.keyboardView?.setKeyboardColor(keyboardColor)
25
63
  }
26
64
  }
27
-
65
+
66
+
28
67
  override init(bridge: RCTBridge) {
29
68
  super.init(bridge: bridge)
30
69
  self.bridge = bridge
31
70
  self.keyboardView = CalculatorKeyboardView()
32
- self.keyboardView!.frame = CGRect(x: 0, y: 0, width: UIScreen.main.bounds.size.width, height: 290 + getbottomInset())
71
+ self.keyboardView!.frame = CGRect(x: 0, y: 0, width: UIScreen.main.bounds.size.width, height: 240 + getbottomInset())
33
72
  self.keyboardView!.input = self
34
-
73
+
35
74
  backedTextInputView.inputView = self.keyboardView
36
75
  backedTextInputView.inputView?.reloadInputViews()
76
+
77
+ NotificationCenter.default.addObserver(
78
+ self,
79
+ selector: #selector(beginEditingInput(_:)),
80
+ name: UITextField.textDidBeginEditingNotification,
81
+ object: backedTextInputView
82
+ )
83
+ NotificationCenter.default.addObserver(
84
+ self,
85
+ selector: #selector(endEditingInput(_:)),
86
+ name: UITextField.textDidEndEditingNotification,
87
+ object: backedTextInputView
88
+ )
89
+
37
90
  }
38
91
  func getbottomInset() -> CGFloat {
39
92
  let window = UIApplication.shared.windows.first?.rootViewController?.view
@@ -44,19 +97,12 @@ class InputCalculator: RCTSinglelineTextInputView {
44
97
  func keyDidPress(_ key: String) {
45
98
  backedTextInputView.insertText(key)
46
99
  value += key
100
+
47
101
  if let bridge = bridge {
48
102
  bridge.eventDispatcher().sendTextEvent(with: .change, reactTag: reactTag, text: value, key: "\(key)", eventCount: 1)
49
103
  }
50
104
  }
51
-
52
- func clearText() {
53
- value = ""
54
- (backedTextInputView as? UITextField)?.text = ""
55
- if let bridge = bridge {
56
- bridge.eventDispatcher().sendTextEvent(with: .change, reactTag: reactTag, text: value, key: "clear", eventCount: 1)
57
- }
58
- }
59
-
105
+
60
106
  func onBackSpace() {
61
107
  value = value.dropLast().description
62
108
  DispatchQueue.main.async {
@@ -72,23 +118,24 @@ class InputCalculator: RCTSinglelineTextInputView {
72
118
  bridge.eventDispatcher().sendTextEvent(with: .change, reactTag: reactTag, text: value, key: "back", eventCount: 1)
73
119
  }
74
120
  }
75
-
121
+
76
122
  func calculateResult() {
77
123
  guard let textField = backedTextInputView as? UITextField,
78
124
  let text = textField.text?.replacingOccurrences(of: "×", with: "*").replacingOccurrences(of: "÷", with: "/")
79
125
  else {
80
126
  return
81
127
  }
82
-
128
+
83
129
  let pattern = "^\\s*(-?\\d+(\\.\\d+)?\\s*[-+*/]\\s*)*-?\\d+(\\.\\d+)?\\s*$"
84
130
  let regex = try? NSRegularExpression(pattern: pattern)
85
131
  let range = NSRange(location: 0, length: text.utf16.count)
86
-
132
+
87
133
  if regex?.firstMatch(in: text, options: [], range: range) != nil {
88
134
  let expression = NSExpression(format: text)
89
135
  if let result = expression.expressionValue(with: nil, context: nil) as? NSNumber {
90
136
  textField.text = result.stringValue
91
137
  value = result.stringValue
138
+
92
139
  if let bridge = bridge {
93
140
  bridge.eventDispatcher().sendTextEvent(with: .change, reactTag: reactTag, text: value, key: "=", eventCount: 1)
94
141
  }
@@ -99,5 +146,13 @@ class InputCalculator: RCTSinglelineTextInputView {
99
146
  }
100
147
  }
101
148
 
149
+ func emitCustomKey() {
150
+ onCustomKeyEvent?([:])
151
+ }
152
+
153
+ func emitKeyPress(_ key: String) {
154
+ onKeyPress?(["key": key])
155
+ }
156
+
102
157
  }
103
158