@momo-kits/calculator-keyboard 0.150.2-beta.20 → 0.150.2-beta.20-sp.2
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 +43 -31
- package/android/src/main/java/com/calculatorkeyboard/RCTInputCalculator.kt +27 -34
- package/ios/CalculatorKeyboardView.h +30 -0
- package/ios/CalculatorKeyboardView.mm +231 -0
- package/ios/NativeInputCalculator.h +11 -0
- package/ios/NativeInputCalculator.mm +200 -0
- package/package.json +75 -59
- package/react-native-calculator-keyboard.podspec +5 -4
- package/src/NativeInputCalculatorNativeComponent.ts +55 -0
- package/src/index.tsx +25 -34
- package/ios/CalculatorKeyboardView.swift +0 -153
- package/ios/InputCalculator-Bridging-Header.h +0 -23
- package/ios/InputCalculator.m +0 -85
- package/ios/InputCalculator.swift +0 -158
- package/ios/extension.swift +0 -23
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
|
|
|
@@ -7,7 +7,6 @@ import android.graphics.drawable.GradientDrawable
|
|
|
7
7
|
import android.view.Gravity
|
|
8
8
|
import android.widget.Button
|
|
9
9
|
import android.widget.ImageButton
|
|
10
|
-
import androidx.appcompat.app.AppCompatActivity
|
|
11
10
|
import androidx.constraintlayout.widget.ConstraintLayout
|
|
12
11
|
import com.facebook.react.uimanager.ThemedReactContext
|
|
13
12
|
import org.mariuszgromada.math.mxparser.Expression
|
|
@@ -21,53 +20,57 @@ class CustomKeyboardView(
|
|
|
21
20
|
context: ThemedReactContext,
|
|
22
21
|
private val editText: CalculatorEditText
|
|
23
22
|
) : ConstraintLayout(context) {
|
|
24
|
-
private val
|
|
23
|
+
private val numWithCTAKeys = listOf(
|
|
25
24
|
listOf("1", "2", "3", "÷", "back"),
|
|
26
25
|
listOf("4", "5", "6", "×", "="),
|
|
27
|
-
listOf("7", "8", "9", "-", "
|
|
26
|
+
listOf("7", "8", "9", "-", "Tiếp"),
|
|
28
27
|
listOf("000", "0", "+")
|
|
29
28
|
)
|
|
30
|
-
private val
|
|
29
|
+
private val defaultKeys = listOf(
|
|
30
|
+
listOf("1", "2", "3", "÷", "AC"),
|
|
31
|
+
listOf("4", "5", "6", "×", "back"),
|
|
32
|
+
listOf("7", "8", "9", "-", "="),
|
|
33
|
+
listOf("000", "0", "+")
|
|
34
|
+
)
|
|
35
|
+
private val specialKeys = listOf("=", "-", "×", "÷", "back", "+", "AC")
|
|
31
36
|
private val separatorWidth = 8f
|
|
32
37
|
private var specialButtonColor: Int = "#D8D8D8".toColorInt()
|
|
33
38
|
|
|
39
|
+
private var keyboardMode: String = "NumDefault"
|
|
40
|
+
|
|
34
41
|
private var customKeyButton: Button? = null
|
|
35
42
|
private var customKeyButtonBackground: Int = "#D8D8D8".toColorInt()
|
|
36
43
|
private var customKeyButtonTextColor: Int = Color.BLACK
|
|
37
44
|
private var customKeyButtonState: String = "default"
|
|
38
45
|
|
|
39
46
|
init {
|
|
40
|
-
val activity = context.currentActivity as? AppCompatActivity
|
|
41
|
-
if (activity != null) {
|
|
42
|
-
val displayMetrics = resources.displayMetrics
|
|
43
|
-
val widthButton = (displayMetrics.widthPixels - separatorWidth * 2 - 4 * separatorWidth) / 5f
|
|
44
|
-
val heightButton = (calculatorHeight - separatorWidth * 2 - 3 * separatorWidth) / 4
|
|
45
|
-
|
|
46
47
|
isClickable = false
|
|
47
48
|
isFocusable = false
|
|
48
49
|
isFocusableInTouchMode = false
|
|
49
50
|
clipToPadding = false
|
|
50
51
|
clipChildren = false
|
|
51
|
-
|
|
52
|
-
renderUI(widthButton, heightButton)
|
|
53
|
-
}
|
|
54
|
-
|
|
55
52
|
}
|
|
56
53
|
|
|
57
|
-
private fun renderUI(
|
|
54
|
+
private fun renderUI() {
|
|
55
|
+
val displayMetrics = resources.displayMetrics
|
|
56
|
+
val buttonWidth = (displayMetrics.widthPixels - separatorWidth * 2 - 4 * separatorWidth) / 5f
|
|
57
|
+
val buttonHeight = (calculatorHeight - separatorWidth * 2 - 3 * separatorWidth) / 4
|
|
58
|
+
|
|
58
59
|
var yOffset = separatorWidth
|
|
60
|
+
val keys = if (keyboardMode == "NumWithCTA") numWithCTAKeys else defaultKeys
|
|
59
61
|
for ((rowIndex, row) in keys.withIndex()) {
|
|
60
62
|
var xOffset = separatorWidth
|
|
61
63
|
for ((colIndex, key) in row.withIndex()) {
|
|
62
|
-
val
|
|
64
|
+
val isMainKey = rowIndex == 2 && colIndex == 4
|
|
65
|
+
val isMainCTAKey = isMainKey && keyboardMode == "NumWithCTA"
|
|
63
66
|
val width = if (key == "000") buttonWidth * 2 + separatorWidth else buttonWidth
|
|
64
|
-
val height = if (
|
|
67
|
+
val height = if (isMainKey) buttonHeight * 2 + separatorWidth else buttonHeight
|
|
65
68
|
|
|
66
69
|
val button = if (key == "back") {
|
|
67
70
|
createImageButton(key, xOffset, yOffset, buttonWidth.toInt(), buttonHeight.toInt())
|
|
68
71
|
} else {
|
|
69
|
-
createButton(key, xOffset, yOffset, width.toInt(), height.toInt(),
|
|
70
|
-
if (
|
|
72
|
+
createButton(key, xOffset, yOffset, width.toInt(), height.toInt(), isMainKey, isMainCTAKey).also { b ->
|
|
73
|
+
if (isMainCTAKey) customKeyButton = b
|
|
71
74
|
}
|
|
72
75
|
}
|
|
73
76
|
|
|
@@ -85,7 +88,8 @@ class CustomKeyboardView(
|
|
|
85
88
|
yOffset: Float,
|
|
86
89
|
buttonWidth: Int,
|
|
87
90
|
buttonHeight: Int,
|
|
88
|
-
|
|
91
|
+
isMainKey: Boolean,
|
|
92
|
+
isMainCTAKey: Boolean
|
|
89
93
|
): Button {
|
|
90
94
|
return Button(context).apply {
|
|
91
95
|
val shapeInit = GradientDrawable().apply {
|
|
@@ -98,7 +102,7 @@ class CustomKeyboardView(
|
|
|
98
102
|
background = shapeInit
|
|
99
103
|
text = key
|
|
100
104
|
setTypeface(typeface)
|
|
101
|
-
textSize = (if (
|
|
105
|
+
textSize = (if (isMainCTAKey) 18 else 24).toFloat()
|
|
102
106
|
setTextColor(Color.BLACK)
|
|
103
107
|
stateListAnimator = null
|
|
104
108
|
maxLines = 1
|
|
@@ -110,7 +114,7 @@ class CustomKeyboardView(
|
|
|
110
114
|
constrainedWidth = false
|
|
111
115
|
}
|
|
112
116
|
|
|
113
|
-
if (specialKeys.contains(key)) {
|
|
117
|
+
if (specialKeys.contains(key) || isMainKey) {
|
|
114
118
|
background = GradientDrawable().apply {
|
|
115
119
|
shape = GradientDrawable.RECTANGLE
|
|
116
120
|
cornerRadius = 24f
|
|
@@ -125,7 +129,7 @@ class CustomKeyboardView(
|
|
|
125
129
|
|
|
126
130
|
translationX = xOffset.toInt().toFloat()
|
|
127
131
|
translationY = yOffset.toInt().toFloat()
|
|
128
|
-
setOnClickListener { onKeyPress(key,
|
|
132
|
+
setOnClickListener { onKeyPress(key, isMainCTAKey) }
|
|
129
133
|
}
|
|
130
134
|
}
|
|
131
135
|
|
|
@@ -195,14 +199,15 @@ class CustomKeyboardView(
|
|
|
195
199
|
}
|
|
196
200
|
}
|
|
197
201
|
|
|
198
|
-
private fun onKeyPress(key: String,
|
|
199
|
-
if (
|
|
202
|
+
private fun onKeyPress(key: String, isMainCTAKey: Boolean) {
|
|
203
|
+
if (isMainCTAKey) {
|
|
200
204
|
emitCustomKey()
|
|
201
205
|
return
|
|
202
206
|
}
|
|
203
207
|
|
|
204
208
|
emitKeyPress(key)
|
|
205
209
|
when (key) {
|
|
210
|
+
"AC" -> clearText()
|
|
206
211
|
"back" -> onBackSpace()
|
|
207
212
|
"=" -> calculateResult()
|
|
208
213
|
"×", "+", "-", "÷" -> keyDidPress(" $key ")
|
|
@@ -215,6 +220,10 @@ class CustomKeyboardView(
|
|
|
215
220
|
editText.text?.replace(editText.selectionStart, editText.selectionEnd, key)
|
|
216
221
|
}
|
|
217
222
|
|
|
223
|
+
private fun clearText() {
|
|
224
|
+
editText.text?.clear()
|
|
225
|
+
}
|
|
226
|
+
|
|
218
227
|
private fun onBackSpace() {
|
|
219
228
|
val start = editText.selectionStart
|
|
220
229
|
val end = editText.selectionEnd
|
|
@@ -270,25 +279,29 @@ class CustomKeyboardView(
|
|
|
270
279
|
customKeyButton?.text = text
|
|
271
280
|
}
|
|
272
281
|
|
|
282
|
+
fun setMode(mode: String) {
|
|
283
|
+
keyboardMode = mode
|
|
284
|
+
renderUI()
|
|
285
|
+
}
|
|
286
|
+
|
|
273
287
|
fun setCustomKeyBackground(background: Int) {
|
|
274
288
|
customKeyButtonBackground = background
|
|
275
|
-
updateCustomKeyUI(background, customKeyButtonTextColor
|
|
289
|
+
updateCustomKeyUI(background, customKeyButtonTextColor)
|
|
276
290
|
}
|
|
277
291
|
|
|
278
292
|
fun setCustomKeyTextColor(textColor: Int) {
|
|
279
293
|
customKeyButtonTextColor = textColor
|
|
280
|
-
updateCustomKeyUI(customKeyButtonBackground, textColor
|
|
294
|
+
updateCustomKeyUI(customKeyButtonBackground, textColor)
|
|
281
295
|
}
|
|
282
296
|
|
|
283
297
|
|
|
284
298
|
fun setCustomKeyState(state: String) {
|
|
285
299
|
customKeyButtonState = state
|
|
286
300
|
customKeyButton?.isEnabled = state != "disable"
|
|
287
|
-
updateCustomKeyUI(customKeyButtonBackground, customKeyButtonTextColor
|
|
301
|
+
updateCustomKeyUI(customKeyButtonBackground, customKeyButtonTextColor)
|
|
288
302
|
}
|
|
289
303
|
|
|
290
|
-
private fun updateCustomKeyUI(background: Int, textColor: Int
|
|
291
|
-
|
|
304
|
+
private fun updateCustomKeyUI(background: Int, textColor: Int){
|
|
292
305
|
customKeyButton?.background = GradientDrawable().apply {
|
|
293
306
|
shape = GradientDrawable.RECTANGLE
|
|
294
307
|
cornerRadius = 24f
|
|
@@ -297,5 +310,4 @@ class CustomKeyboardView(
|
|
|
297
310
|
customKeyButton?.setTextColor(textColor)
|
|
298
311
|
}
|
|
299
312
|
|
|
300
|
-
|
|
301
313
|
}
|
|
@@ -8,15 +8,20 @@ import android.view.inputmethod.InputMethodManager
|
|
|
8
8
|
import androidx.core.graphics.toColorInt
|
|
9
9
|
import androidx.core.view.ViewCompat
|
|
10
10
|
import androidx.core.view.WindowInsetsCompat
|
|
11
|
-
import com.facebook.react.bridge.ReadableArray
|
|
12
11
|
import com.facebook.react.bridge.UiThreadUtil
|
|
13
12
|
import com.facebook.react.uimanager.PixelUtil.dpToPx
|
|
14
13
|
import com.facebook.react.uimanager.ThemedReactContext
|
|
15
|
-
import com.facebook.react.uimanager.annotations.ReactProp
|
|
16
14
|
import com.facebook.react.views.textinput.ReactEditText
|
|
17
15
|
import com.facebook.react.views.textinput.ReactTextInputManager
|
|
16
|
+
import com.facebook.react.viewmanagers.NativeInputCalculatorManagerDelegate
|
|
17
|
+
import com.facebook.react.viewmanagers.NativeInputCalculatorManagerInterface
|
|
18
|
+
import com.facebook.react.uimanager.ViewManagerDelegate
|
|
19
|
+
import androidx.annotation.Nullable
|
|
18
20
|
|
|
19
|
-
|
|
21
|
+
// Fabric-only manager with codegen name `NativeInputCalculator`
|
|
22
|
+
class InputCalculatorManager : ReactTextInputManager(), NativeInputCalculatorManagerInterface<ReactEditText> {
|
|
23
|
+
|
|
24
|
+
private val mDelegate: ViewManagerDelegate<ReactEditText> = NativeInputCalculatorManagerDelegate(this)
|
|
20
25
|
|
|
21
26
|
private var keyboardView: CustomKeyboardView? = null
|
|
22
27
|
private lateinit var editText: CalculatorEditText
|
|
@@ -30,12 +35,11 @@ class RCTInputCalculator : ReactTextInputManager() {
|
|
|
30
35
|
override fun getName() = REACT_CLASS
|
|
31
36
|
|
|
32
37
|
companion object {
|
|
33
|
-
const val REACT_CLASS = "
|
|
38
|
+
const val REACT_CLASS = "NativeInputCalculator"
|
|
34
39
|
val calculatorHeight: Int = 240.dpToPx().toInt()
|
|
35
40
|
}
|
|
36
41
|
|
|
37
|
-
|
|
38
|
-
fun setValue(view: ReactEditText, value: String?) {
|
|
42
|
+
override fun setValue(view: ReactEditText, @Nullable value: String?) {
|
|
39
43
|
UiThreadUtil.runOnUiThread {
|
|
40
44
|
val e = view.editableText ?: run { view.setText(value ?: ""); return@runOnUiThread }
|
|
41
45
|
val newText = value ?: ""
|
|
@@ -48,29 +52,28 @@ class RCTInputCalculator : ReactTextInputManager() {
|
|
|
48
52
|
}
|
|
49
53
|
}
|
|
50
54
|
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
55
|
+
override fun setMode(view: ReactEditText, @Nullable mode: String?) {
|
|
56
|
+
keyboardView?.setMode(mode ?: "NumDefault")
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
override fun setCustomKeyText(view: ReactEditText, @Nullable text: String?) {
|
|
60
|
+
keyboardView?.setCustomKeyText(text ?: "Tiếp")
|
|
54
61
|
}
|
|
55
62
|
|
|
56
|
-
|
|
57
|
-
fun setCustomKeyBackground(view: ReactEditText, background: String?) {
|
|
63
|
+
override fun setCustomKeyBackground(view: ReactEditText, @Nullable background: String?) {
|
|
58
64
|
keyboardView?.setCustomKeyBackground((background ?: "#d8d8d8").toColorInt())
|
|
59
65
|
}
|
|
60
66
|
|
|
61
|
-
|
|
62
|
-
fun setCustomKeyTextColor(view: ReactEditText, textColor: String?) {
|
|
67
|
+
override fun setCustomKeyTextColor(view: ReactEditText, @Nullable textColor: String?) {
|
|
63
68
|
keyboardView?.setCustomKeyTextColor(textColor?.toColorInt() ?: Color.BLACK)
|
|
64
69
|
}
|
|
65
70
|
|
|
66
|
-
|
|
67
|
-
fun setCustomKeyState(view: ReactEditText, state: String?) {
|
|
71
|
+
override fun setCustomKeyState(view: ReactEditText, @Nullable state: String?) {
|
|
68
72
|
keyboardView?.setCustomKeyState(state ?: "default")
|
|
69
73
|
}
|
|
70
74
|
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
keyboardView?.updateButtonColors(color.toColorInt())
|
|
75
|
+
override fun setKeyboardColor(view: ReactEditText, @Nullable color: Int?) {
|
|
76
|
+
color?.let { keyboardView?.updateButtonColors(it) }
|
|
74
77
|
}
|
|
75
78
|
|
|
76
79
|
@SuppressLint("ClickableViewAccessibility")
|
|
@@ -165,26 +168,17 @@ class RCTInputCalculator : ReactTextInputManager() {
|
|
|
165
168
|
v.showSoftInputOnFocus = true
|
|
166
169
|
}
|
|
167
170
|
|
|
168
|
-
override fun
|
|
171
|
+
override fun getDelegate(): ViewManagerDelegate<ReactEditText> = mDelegate
|
|
169
172
|
|
|
170
|
-
override fun
|
|
171
|
-
|
|
172
|
-
1 -> blur()
|
|
173
|
-
2 -> focus()
|
|
174
|
-
}
|
|
173
|
+
override fun focus(view: ReactEditText) {
|
|
174
|
+
UiThreadUtil.runOnUiThread { if (!editText.isFocused) editText.requestFocus() }
|
|
175
175
|
}
|
|
176
176
|
|
|
177
|
-
override fun
|
|
178
|
-
|
|
179
|
-
base["onKeyPress"] = mapOf("phasedRegistrationNames" to mapOf("bubbled" to "onKeyPress"))
|
|
180
|
-
return base
|
|
177
|
+
override fun blur(view: ReactEditText) {
|
|
178
|
+
UiThreadUtil.runOnUiThread { if (editText.isFocused) editText.clearFocus() }
|
|
181
179
|
}
|
|
182
180
|
|
|
183
|
-
|
|
184
|
-
val base = super.getExportedCustomDirectEventTypeConstants().toMutableMap()
|
|
185
|
-
base["onCustomKeyEvent"] = mapOf("registrationName" to "onCustomKeyEvent")
|
|
186
|
-
return base
|
|
187
|
-
}
|
|
181
|
+
// Events are emitted from CustomKeyboardView using RCTEventEmitter.
|
|
188
182
|
|
|
189
183
|
private fun focus() {
|
|
190
184
|
UiThreadUtil.runOnUiThread { if (!editText.isFocused) editText.requestFocus() }
|
|
@@ -194,4 +188,3 @@ class RCTInputCalculator : ReactTextInputManager() {
|
|
|
194
188
|
UiThreadUtil.runOnUiThread { if (editText.isFocused) editText.clearFocus() }
|
|
195
189
|
}
|
|
196
190
|
}
|
|
197
|
-
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
#import <UIKit/UIKit.h>
|
|
2
|
+
|
|
3
|
+
NS_ASSUME_NONNULL_BEGIN
|
|
4
|
+
|
|
5
|
+
@protocol CalculatorKeyboardInput <NSObject>
|
|
6
|
+
|
|
7
|
+
- (void)keyDidPress:(NSString *)key;
|
|
8
|
+
- (void)clearText;
|
|
9
|
+
- (void)onBackSpace;
|
|
10
|
+
- (void)calculateResult;
|
|
11
|
+
- (void)emitCustomKey;
|
|
12
|
+
- (void)emitKeyPress:(NSString *)key;
|
|
13
|
+
|
|
14
|
+
@end
|
|
15
|
+
|
|
16
|
+
@interface CalculatorKeyboardView : UIView
|
|
17
|
+
|
|
18
|
+
@property (nonatomic, weak, nullable) id<CalculatorKeyboardInput> input;
|
|
19
|
+
@property (nonatomic, strong, nullable) NSString *keyboardMode;
|
|
20
|
+
@property (nonatomic, strong, nullable) NSString *customKeyText;
|
|
21
|
+
@property (nonatomic, strong, nullable) NSString *customKeyBackground;
|
|
22
|
+
@property (nonatomic, strong, nullable) NSString *customKeyTextColor;
|
|
23
|
+
@property (nonatomic, strong, nullable) NSString *customKeyState;
|
|
24
|
+
|
|
25
|
+
- (void)setKeyboardColor:(UIColor *)color;
|
|
26
|
+
|
|
27
|
+
@end
|
|
28
|
+
|
|
29
|
+
NS_ASSUME_NONNULL_END
|
|
30
|
+
|
|
@@ -0,0 +1,231 @@
|
|
|
1
|
+
#import "CalculatorKeyboardView.h"
|
|
2
|
+
|
|
3
|
+
@interface CalculatorKeyboardView ()
|
|
4
|
+
@property (nonatomic, strong) NSArray<NSArray<NSString *> *> *numWithCTAKeys;
|
|
5
|
+
@property (nonatomic, strong) NSArray<NSArray<NSString *> *> *defaultKeys;
|
|
6
|
+
@property (nonatomic, strong) NSSet<NSString *> *specialKeys;
|
|
7
|
+
@property (nonatomic, weak) UIButton *customKeyButton;
|
|
8
|
+
@end
|
|
9
|
+
|
|
10
|
+
@implementation CalculatorKeyboardView {
|
|
11
|
+
CGFloat _separatorWidth;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
- (instancetype)initWithFrame:(CGRect)frame
|
|
15
|
+
{
|
|
16
|
+
if (self = [super initWithFrame:frame]) {
|
|
17
|
+
_separatorWidth = 4.0;
|
|
18
|
+
|
|
19
|
+
_numWithCTAKeys = @[
|
|
20
|
+
@[@"1", @"2", @"3", @"÷", @"back"],
|
|
21
|
+
@[@"4", @"5", @"6", @"×", @"="],
|
|
22
|
+
@[@"7", @"8", @"9", @"-", @"Tiếp"],
|
|
23
|
+
@[@"000", @"0", @"+"]
|
|
24
|
+
];
|
|
25
|
+
|
|
26
|
+
_defaultKeys = @[
|
|
27
|
+
@[@"1", @"2", @"3", @"÷", @"AC"],
|
|
28
|
+
@[@"4", @"5", @"6", @"×", @"back"],
|
|
29
|
+
@[@"7", @"8", @"9", @"-", @"="],
|
|
30
|
+
@[@"000", @"0", @"+"]
|
|
31
|
+
];
|
|
32
|
+
|
|
33
|
+
_specialKeys = [NSSet setWithArray:@[@"=", @"-", @"×", @"÷", @"back", @"+", @"AC"]];
|
|
34
|
+
|
|
35
|
+
[self setup];
|
|
36
|
+
}
|
|
37
|
+
return self;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
- (void)setKeyboardMode:(NSString *)keyboardMode
|
|
41
|
+
{
|
|
42
|
+
if ([_keyboardMode isEqualToString:keyboardMode]) {
|
|
43
|
+
return;
|
|
44
|
+
}
|
|
45
|
+
_keyboardMode = keyboardMode;
|
|
46
|
+
[self setup];
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
- (void)setCustomKeyText:(NSString *)customKeyText
|
|
50
|
+
{
|
|
51
|
+
_customKeyText = customKeyText;
|
|
52
|
+
[self updateCustomKeyTitle];
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
- (void)setCustomKeyBackground:(NSString *)customKeyBackground
|
|
56
|
+
{
|
|
57
|
+
_customKeyBackground = customKeyBackground;
|
|
58
|
+
[self updateCustomKeyBackground];
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
- (void)setCustomKeyTextColor:(NSString *)customKeyTextColor
|
|
62
|
+
{
|
|
63
|
+
_customKeyTextColor = customKeyTextColor;
|
|
64
|
+
[self updateCustomKeyBackground];
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
- (void)setCustomKeyState:(NSString *)customKeyState
|
|
68
|
+
{
|
|
69
|
+
_customKeyState = customKeyState;
|
|
70
|
+
[self updateCustomKeyBackground];
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
- (void)setKeyboardColor:(UIColor *)color
|
|
74
|
+
{
|
|
75
|
+
[self setup];
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
- (void)setup
|
|
79
|
+
{
|
|
80
|
+
// Remove all subviews
|
|
81
|
+
for (UIView *subview in self.subviews) {
|
|
82
|
+
[subview removeFromSuperview];
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
self.backgroundColor = [self colorFromHex:@"#f2f2f6"];
|
|
86
|
+
|
|
87
|
+
CGFloat buttonWidth = (UIScreen.mainScreen.bounds.size.width - _separatorWidth * 2 - 4 * _separatorWidth) / 5;
|
|
88
|
+
CGFloat buttonHeight = (240 - _separatorWidth * 2 - 3 * _separatorWidth) / 4;
|
|
89
|
+
|
|
90
|
+
// Create content view
|
|
91
|
+
UIView *contentView = [[UIView alloc] init];
|
|
92
|
+
contentView.translatesAutoresizingMaskIntoConstraints = NO;
|
|
93
|
+
[self addSubview:contentView];
|
|
94
|
+
|
|
95
|
+
// Set contentView constraints
|
|
96
|
+
[NSLayoutConstraint activateConstraints:@[
|
|
97
|
+
[contentView.leadingAnchor constraintEqualToAnchor:self.leadingAnchor constant:_separatorWidth],
|
|
98
|
+
[contentView.trailingAnchor constraintEqualToAnchor:self.trailingAnchor constant:-_separatorWidth],
|
|
99
|
+
[contentView.topAnchor constraintEqualToAnchor:self.topAnchor constant:_separatorWidth],
|
|
100
|
+
[contentView.bottomAnchor constraintEqualToAnchor:self.bottomAnchor constant:-_separatorWidth]
|
|
101
|
+
]];
|
|
102
|
+
|
|
103
|
+
CGFloat yOffset = 0;
|
|
104
|
+
NSArray<NSArray<NSString *> *> *keys = [_keyboardMode isEqualToString:@"NumDefault"] ? _defaultKeys : _numWithCTAKeys;
|
|
105
|
+
|
|
106
|
+
for (NSInteger rowIndex = 0; rowIndex < keys.count; rowIndex++) {
|
|
107
|
+
NSArray<NSString *> *row = keys[rowIndex];
|
|
108
|
+
CGFloat xOffset = 0;
|
|
109
|
+
|
|
110
|
+
for (NSInteger colIndex = 0; colIndex < row.count; colIndex++) {
|
|
111
|
+
NSString *key = row[colIndex];
|
|
112
|
+
BOOL isMainKey = (colIndex == 4 && rowIndex == 2);
|
|
113
|
+
BOOL isMainCTAKey = isMainKey && [_keyboardMode isEqualToString:@"NumWithCTA"];
|
|
114
|
+
|
|
115
|
+
UIButton *button = [UIButton buttonWithType:UIButtonTypeSystem];
|
|
116
|
+
button.backgroundColor = UIColor.whiteColor;
|
|
117
|
+
button.layer.cornerRadius = 8;
|
|
118
|
+
|
|
119
|
+
NSString *title = isMainCTAKey ? (_customKeyText ?: key) : key;
|
|
120
|
+
[button setTitle:title forState:UIControlStateNormal];
|
|
121
|
+
[button setTitleColor:UIColor.blackColor forState:UIControlStateNormal];
|
|
122
|
+
button.titleLabel.font = [UIFont systemFontOfSize:isMainCTAKey ? 18 : 24 weight:UIFontWeightMedium];
|
|
123
|
+
button.accessibilityIdentifier = key;
|
|
124
|
+
button.tag = isMainCTAKey ? 1 : 0;
|
|
125
|
+
|
|
126
|
+
CGRect buttonFrame = CGRectMake(xOffset, yOffset, buttonWidth, buttonHeight);
|
|
127
|
+
if (isMainKey) {
|
|
128
|
+
buttonFrame.size.height = buttonHeight * 2 + _separatorWidth;
|
|
129
|
+
}
|
|
130
|
+
if ([key isEqualToString:@"000"]) {
|
|
131
|
+
buttonFrame.size.width = buttonWidth * 2 + _separatorWidth;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
button.frame = buttonFrame;
|
|
135
|
+
|
|
136
|
+
if ([key isEqualToString:@"back"]) {
|
|
137
|
+
[button setTitle:@"" forState:UIControlStateNormal];
|
|
138
|
+
UIImageSymbolConfiguration *config = [UIImageSymbolConfiguration configurationWithWeight:UIImageSymbolWeightBold];
|
|
139
|
+
UIImage *image = [UIImage systemImageNamed:@"delete.backward" withConfiguration:config];
|
|
140
|
+
[button setImage:image forState:UIControlStateNormal];
|
|
141
|
+
button.tintColor = UIColor.blackColor;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
if ([_specialKeys containsObject:key] || isMainKey) {
|
|
145
|
+
[button setTitleColor:UIColor.blackColor forState:UIControlStateNormal];
|
|
146
|
+
button.backgroundColor = [self colorFromHex:@"#d8d8d8"];
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
if (isMainKey) {
|
|
150
|
+
self.customKeyButton = button;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
[button addTarget:self action:@selector(keyPressed:) forControlEvents:UIControlEventTouchUpInside];
|
|
154
|
+
[contentView addSubview:button];
|
|
155
|
+
|
|
156
|
+
xOffset += buttonFrame.size.width + _separatorWidth;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
yOffset += buttonHeight + _separatorWidth;
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
- (void)updateCustomKeyTitle
|
|
164
|
+
{
|
|
165
|
+
if (!self.customKeyButton || !_customKeyText) {
|
|
166
|
+
return;
|
|
167
|
+
}
|
|
168
|
+
[self.customKeyButton setTitle:_customKeyText forState:UIControlStateNormal];
|
|
169
|
+
[self.customKeyButton setImage:nil forState:UIControlStateNormal];
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
- (void)updateCustomKeyBackground
|
|
173
|
+
{
|
|
174
|
+
if (!self.customKeyButton || !_keyboardMode || !_customKeyBackground || !_customKeyTextColor || !_customKeyState) {
|
|
175
|
+
return;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
if ([_keyboardMode isEqualToString:@"numWithCTA"]) {
|
|
179
|
+
self.customKeyButton.enabled = ![_customKeyState isEqualToString:@"disable"];
|
|
180
|
+
} else {
|
|
181
|
+
self.customKeyButton.enabled = YES;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
self.customKeyButton.backgroundColor = [self colorFromHex:_customKeyBackground];
|
|
185
|
+
[self.customKeyButton setTitleColor:[self colorFromHex:_customKeyTextColor] forState:UIControlStateNormal];
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
- (void)keyPressed:(UIButton *)sender
|
|
189
|
+
{
|
|
190
|
+
NSString *key = sender.accessibilityIdentifier;
|
|
191
|
+
if (!key) {
|
|
192
|
+
return;
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
BOOL isCustomKeyCTA = (sender.tag == 1);
|
|
196
|
+
if (isCustomKeyCTA) {
|
|
197
|
+
[self.input emitCustomKey];
|
|
198
|
+
return;
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
[self.input emitKeyPress:key];
|
|
202
|
+
|
|
203
|
+
if ([key isEqualToString:@"AC"]) {
|
|
204
|
+
[self.input clearText];
|
|
205
|
+
} else if ([key isEqualToString:@"back"]) {
|
|
206
|
+
[self.input onBackSpace];
|
|
207
|
+
} else if ([key isEqualToString:@"="]) {
|
|
208
|
+
[self.input calculateResult];
|
|
209
|
+
} else if ([key isEqualToString:@"+"] || [key isEqualToString:@"-"] ||
|
|
210
|
+
[key isEqualToString:@"÷"] || [key isEqualToString:@"×"]) {
|
|
211
|
+
[self.input keyDidPress:[NSString stringWithFormat:@" %@ ", key]];
|
|
212
|
+
} else {
|
|
213
|
+
[self.input keyDidPress:key];
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
- (UIColor *)colorFromHex:(NSString *)hexString
|
|
218
|
+
{
|
|
219
|
+
unsigned rgbValue = 0;
|
|
220
|
+
NSString *cleanHex = [hexString stringByReplacingOccurrencesOfString:@"#" withString:@""];
|
|
221
|
+
NSScanner *scanner = [NSScanner scannerWithString:cleanHex];
|
|
222
|
+
[scanner scanHexInt:&rgbValue];
|
|
223
|
+
|
|
224
|
+
return [UIColor colorWithRed:((rgbValue & 0xFF0000) >> 16) / 255.0
|
|
225
|
+
green:((rgbValue & 0x00FF00) >> 8) / 255.0
|
|
226
|
+
blue:(rgbValue & 0x0000FF) / 255.0
|
|
227
|
+
alpha:1.0];
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
@end
|
|
231
|
+
|