@momo-kits/calculator-keyboard 0.150.2-beta.21 → 0.150.2-beta.22
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 +3 -45
- package/android/src/main/java/com/calculatorkeyboard/CalculatorKeyboardPackage.kt +1 -1
- package/android/src/main/java/com/calculatorkeyboard/CustomKeyboardView.kt +77 -68
- package/android/src/main/java/com/calculatorkeyboard/{InputCalculatorViewManager.kt → RCTInputCalculator.kt} +27 -100
- package/ios/CalculatorKeyboardView.swift +153 -0
- package/ios/InputCalculator-Bridging-Header.h +23 -0
- package/ios/InputCalculator.m +85 -0
- package/ios/InputCalculator.swift +158 -0
- package/ios/extension.swift +23 -0
- package/package.json +8 -19
- package/react-native-calculator-keyboard.podspec +4 -5
- package/src/index.tsx +30 -58
- package/android/src/main/java/com/calculatorkeyboard/Event.kt +0 -36
- package/ios/CalculatorKeyboardView.h +0 -30
- package/ios/CalculatorKeyboardView.mm +0 -231
- package/ios/NativeInputCalculator.h +0 -11
- package/ios/NativeInputCalculator.mm +0 -230
- package/src/InputCalculatorNativeComponent.ts +0 -62
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
import UIKit
|
|
2
|
+
import Foundation
|
|
3
|
+
|
|
4
|
+
import UIKit
|
|
5
|
+
import Foundation
|
|
6
|
+
|
|
7
|
+
class CalculatorKeyboardView: UIView {
|
|
8
|
+
weak var input: InputCalculator?
|
|
9
|
+
|
|
10
|
+
private let keys: [[String]] = [
|
|
11
|
+
["1", "2", "3", "÷", "back"],
|
|
12
|
+
["4", "5", "6", "×", "="],
|
|
13
|
+
["7", "8", "9", "-", "Xong"],
|
|
14
|
+
["000", "0", "+"],
|
|
15
|
+
]
|
|
16
|
+
private let SEPARATOR_WIDTH: CGFloat = 4
|
|
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
|
+
|
|
25
|
+
|
|
26
|
+
override init(frame: CGRect) {
|
|
27
|
+
super.init(frame: frame)
|
|
28
|
+
setup()
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
required init?(coder aDecoder: NSCoder) {
|
|
32
|
+
fatalError("init(coder:) has not been implemented")
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
public func setKeyboardColor(_ color: UIColor) {
|
|
36
|
+
self.setup(color)
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
private func setup(_ color: UIColor = UIColor(hex: "#d8d8d8")) {
|
|
40
|
+
self.subviews.forEach { $0.removeFromSuperview() }
|
|
41
|
+
|
|
42
|
+
backgroundColor = UIColor(hex: "#f2f2f6")
|
|
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
|
|
45
|
+
|
|
46
|
+
// Create a wrapper view
|
|
47
|
+
let contentView = UIView()
|
|
48
|
+
contentView.translatesAutoresizingMaskIntoConstraints = false
|
|
49
|
+
addSubview(contentView)
|
|
50
|
+
|
|
51
|
+
// Set contentView constraints
|
|
52
|
+
NSLayoutConstraint.activate([
|
|
53
|
+
contentView.leadingAnchor.constraint(equalTo: leadingAnchor, constant: SEPARATOR_WIDTH),
|
|
54
|
+
contentView.trailingAnchor.constraint(equalTo: trailingAnchor, constant: -SEPARATOR_WIDTH),
|
|
55
|
+
contentView.topAnchor.constraint(equalTo: topAnchor, constant: SEPARATOR_WIDTH),
|
|
56
|
+
contentView.bottomAnchor.constraint(equalTo: bottomAnchor, constant: -SEPARATOR_WIDTH)
|
|
57
|
+
])
|
|
58
|
+
|
|
59
|
+
// Add buttons to the wrapper view
|
|
60
|
+
var yOffset: CGFloat = 0
|
|
61
|
+
for (rowIndex, row) in keys.enumerated() {
|
|
62
|
+
var xOffset: CGFloat = 0
|
|
63
|
+
for (colIndex, key) in row.enumerated() {
|
|
64
|
+
let isCustomKey = colIndex == 4 && rowIndex == 2
|
|
65
|
+
let button = UIButton(type: .system)
|
|
66
|
+
button.backgroundColor = UIColor.white
|
|
67
|
+
button.layer.cornerRadius = 8
|
|
68
|
+
let title = isCustomKey ? (customKeyText ?? key) : key
|
|
69
|
+
button.setTitle(title, for: .normal)
|
|
70
|
+
button.setTitleColor(.black, for: .normal)
|
|
71
|
+
button.titleLabel?.font = UIFont.systemFont(ofSize: isCustomKey ? 18 : 24, weight: .medium)
|
|
72
|
+
button.nativeID = key
|
|
73
|
+
button.tag = isCustomKey ? 1 : 0
|
|
74
|
+
|
|
75
|
+
var buttonFrame = CGRect(x: xOffset, y: yOffset, width: buttonWidth, height: buttonHeight)
|
|
76
|
+
if isCustomKey {
|
|
77
|
+
buttonFrame.size.height = buttonHeight * 2 + SEPARATOR_WIDTH
|
|
78
|
+
}
|
|
79
|
+
if key == "000" {
|
|
80
|
+
buttonFrame.size.width = buttonWidth * 2 + SEPARATOR_WIDTH
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
button.frame = buttonFrame
|
|
84
|
+
|
|
85
|
+
if key == "back" {
|
|
86
|
+
button.setTitle("", for: .normal)
|
|
87
|
+
let image = UIImage(systemName: "delete.backward", withConfiguration: UIImage.SymbolConfiguration(weight: .bold))
|
|
88
|
+
button.setImage(image, for: .normal)
|
|
89
|
+
button.tintColor = .black
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
if specialKeys.contains(key) {
|
|
93
|
+
button.setTitleColor(.black, for: .normal)
|
|
94
|
+
button.backgroundColor = color
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
if isCustomKey {
|
|
98
|
+
self.customKeyButton = button
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
button.addTarget(self, action: #selector(keyPressed(_:)), for: .touchUpInside)
|
|
102
|
+
contentView.addSubview(button)
|
|
103
|
+
|
|
104
|
+
// Adjust xOffset for the next button in the row
|
|
105
|
+
xOffset += buttonFrame.width + SEPARATOR_WIDTH
|
|
106
|
+
}
|
|
107
|
+
// Adjust yOffset for the next row
|
|
108
|
+
yOffset += buttonHeight + SEPARATOR_WIDTH
|
|
109
|
+
}
|
|
110
|
+
}
|
|
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
|
+
|
|
132
|
+
|
|
133
|
+
@objc private func keyPressed(_ sender: UIButton) {
|
|
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)
|
|
142
|
+
switch key {
|
|
143
|
+
case "back":
|
|
144
|
+
input?.onBackSpace()
|
|
145
|
+
case "=":
|
|
146
|
+
input?.calculateResult()
|
|
147
|
+
case "+", "-", "÷", "×":
|
|
148
|
+
input?.keyDidPress(" \(key) ")
|
|
149
|
+
default:
|
|
150
|
+
input?.keyDidPress(key)
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
#import <React/RCTBridgeModule.h>
|
|
2
|
+
#import <React/RCTViewManager.h>
|
|
3
|
+
#import <React/RCTBaseTextInputViewManager.h>
|
|
4
|
+
#import <React/RCTSinglelineTextInputView.h>
|
|
5
|
+
#import <React/RCTUITextField.h>
|
|
6
|
+
#import <React/RCTBaseTextInputViewManager.h>
|
|
7
|
+
|
|
8
|
+
#import <React/RCTBridge.h>
|
|
9
|
+
#import <React/RCTConvert.h>
|
|
10
|
+
#import <React/RCTFont.h>
|
|
11
|
+
#import <React/RCTShadowView+Layout.h>
|
|
12
|
+
#import <React/RCTShadowView.h>
|
|
13
|
+
#import <React/RCTUIManager.h>
|
|
14
|
+
#import <React/RCTUIManagerObserverCoordinator.h>
|
|
15
|
+
#import <React/RCTUIManagerUtils.h>
|
|
16
|
+
|
|
17
|
+
#import <React/RCTBaseTextInputShadowView.h>
|
|
18
|
+
#import <React/RCTBaseTextInputView.h>
|
|
19
|
+
#import <React/RCTConvert+Text.h>
|
|
20
|
+
|
|
21
|
+
@interface Calculator : NSObject <RCTBridgeModule>
|
|
22
|
+
|
|
23
|
+
@end
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
#import <InputCalculator-Bridging-Header.h>
|
|
2
|
+
|
|
3
|
+
@implementation Calculator
|
|
4
|
+
|
|
5
|
+
- (dispatch_queue_t)methodQueue
|
|
6
|
+
{
|
|
7
|
+
return dispatch_get_main_queue();
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
RCT_EXPORT_MODULE()
|
|
11
|
+
|
|
12
|
+
@end
|
|
13
|
+
|
|
14
|
+
@interface RCT_EXTERN_MODULE(RCTInputCalculator, RCTViewManager)
|
|
15
|
+
|
|
16
|
+
RCT_REMAP_VIEW_PROPERTY(autoCapitalize, backedTextInputView.autocapitalizationType, UITextAutocapitalizationType)
|
|
17
|
+
RCT_REMAP_VIEW_PROPERTY(autoCorrect, backedTextInputView.autocorrectionType, UITextAutocorrectionType)
|
|
18
|
+
RCT_REMAP_VIEW_PROPERTY(contextMenuHidden, backedTextInputView.contextMenuHidden, BOOL)
|
|
19
|
+
RCT_REMAP_VIEW_PROPERTY(editable, backedTextInputView.editable, BOOL)
|
|
20
|
+
RCT_REMAP_VIEW_PROPERTY(enablesReturnKeyAutomatically, backedTextInputView.enablesReturnKeyAutomatically, BOOL)
|
|
21
|
+
RCT_REMAP_VIEW_PROPERTY(keyboardAppearance, backedTextInputView.keyboardAppearance, UIKeyboardAppearance)
|
|
22
|
+
RCT_REMAP_VIEW_PROPERTY(placeholder, backedTextInputView.placeholder, NSString)
|
|
23
|
+
RCT_REMAP_VIEW_PROPERTY(placeholderTextColor, backedTextInputView.placeholderColor, UIColor)
|
|
24
|
+
RCT_REMAP_VIEW_PROPERTY(returnKeyType, backedTextInputView.returnKeyType, UIReturnKeyType)
|
|
25
|
+
RCT_REMAP_VIEW_PROPERTY(selectionColor, backedTextInputView.tintColor, UIColor)
|
|
26
|
+
RCT_REMAP_VIEW_PROPERTY(spellCheck, backedTextInputView.spellCheckingType, UITextSpellCheckingType)
|
|
27
|
+
RCT_REMAP_VIEW_PROPERTY(caretHidden, backedTextInputView.caretHidden, BOOL)
|
|
28
|
+
RCT_REMAP_VIEW_PROPERTY(clearButtonMode, backedTextInputView.clearButtonMode, UITextFieldViewMode)
|
|
29
|
+
RCT_REMAP_VIEW_PROPERTY(scrollEnabled, backedTextInputView.scrollEnabled, BOOL)
|
|
30
|
+
RCT_REMAP_VIEW_PROPERTY(secureTextEntry, backedTextInputView.secureTextEntry, BOOL)
|
|
31
|
+
RCT_REMAP_VIEW_PROPERTY(smartInsertDelete, backedTextInputView.smartInsertDeleteType, UITextSmartInsertDeleteType)
|
|
32
|
+
|
|
33
|
+
RCT_EXPORT_VIEW_PROPERTY(autoFocus, BOOL)
|
|
34
|
+
RCT_EXPORT_VIEW_PROPERTY(submitBehavior, NSString)
|
|
35
|
+
RCT_EXPORT_VIEW_PROPERTY(clearTextOnFocus, BOOL)
|
|
36
|
+
RCT_EXPORT_VIEW_PROPERTY(keyboardType, UIKeyboardType)
|
|
37
|
+
RCT_EXPORT_VIEW_PROPERTY(showSoftInputOnFocus, BOOL)
|
|
38
|
+
RCT_EXPORT_VIEW_PROPERTY(maxLength, NSNumber)
|
|
39
|
+
RCT_EXPORT_VIEW_PROPERTY(selectTextOnFocus, BOOL)
|
|
40
|
+
RCT_EXPORT_VIEW_PROPERTY(selection, RCTTextSelection)
|
|
41
|
+
RCT_EXPORT_VIEW_PROPERTY(inputAccessoryViewID, NSString)
|
|
42
|
+
RCT_EXPORT_VIEW_PROPERTY(textContentType, NSString)
|
|
43
|
+
RCT_EXPORT_VIEW_PROPERTY(passwordRules, NSString)
|
|
44
|
+
|
|
45
|
+
RCT_EXPORT_VIEW_PROPERTY(onChange, RCTBubblingEventBlock)
|
|
46
|
+
RCT_EXPORT_VIEW_PROPERTY(onKeyPressSync, RCTDirectEventBlock)
|
|
47
|
+
RCT_EXPORT_VIEW_PROPERTY(onChangeSync, RCTDirectEventBlock)
|
|
48
|
+
RCT_EXPORT_VIEW_PROPERTY(onSelectionChange, RCTDirectEventBlock)
|
|
49
|
+
RCT_EXPORT_VIEW_PROPERTY(onScroll, RCTDirectEventBlock)
|
|
50
|
+
|
|
51
|
+
RCT_EXPORT_VIEW_PROPERTY(mostRecentEventCount, NSInteger)
|
|
52
|
+
|
|
53
|
+
RCT_EXPORT_SHADOW_PROPERTY(text, NSString)
|
|
54
|
+
RCT_EXPORT_SHADOW_PROPERTY(placeholder, NSString)
|
|
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)
|
|
61
|
+
|
|
62
|
+
RCT_EXPORT_METHOD(focus : (nonnull NSNumber *)viewTag)
|
|
63
|
+
{
|
|
64
|
+
[self.bridge.uiManager addUIBlock:^(RCTUIManager *uiManager, NSDictionary<NSNumber *, UIView *> *viewRegistry) {
|
|
65
|
+
UIView *view = viewRegistry[viewTag];
|
|
66
|
+
[view reactFocus];
|
|
67
|
+
}];
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
RCT_EXPORT_METHOD(blur : (nonnull NSNumber *)viewTag)
|
|
71
|
+
{
|
|
72
|
+
[self.bridge.uiManager addUIBlock:^(RCTUIManager *uiManager, NSDictionary<NSNumber *, UIView *> *viewRegistry) {
|
|
73
|
+
UIView *view = viewRegistry[viewTag];
|
|
74
|
+
[view reactBlur];
|
|
75
|
+
}];
|
|
76
|
+
}
|
|
77
|
+
|
|
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)
|
|
83
|
+
|
|
84
|
+
@end
|
|
85
|
+
|
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
import Foundation
|
|
2
|
+
import UIKit
|
|
3
|
+
|
|
4
|
+
@objc(RCTInputCalculator)
|
|
5
|
+
class RCTInputCalculator: RCTBaseTextInputViewManager {
|
|
6
|
+
override func view() -> UIView! {
|
|
7
|
+
return InputCalculator(bridge: bridge)
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
override static func requiresMainQueueSetup() -> Bool {
|
|
11
|
+
return true
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
class InputCalculator: RCTSinglelineTextInputView {
|
|
17
|
+
var bridge: RCTBridge?
|
|
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
|
+
}
|
|
28
|
+
|
|
29
|
+
@objc var customKeyBackground: String? {
|
|
30
|
+
didSet { keyboardView?.customKeyBackground = customKeyBackground }
|
|
31
|
+
}
|
|
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
|
+
|
|
60
|
+
@objc var keyboardColor: UIColor = UIColor(hex: "#d9d9d9") {
|
|
61
|
+
didSet {
|
|
62
|
+
self.keyboardView?.setKeyboardColor(keyboardColor)
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
override init(bridge: RCTBridge) {
|
|
68
|
+
super.init(bridge: bridge)
|
|
69
|
+
self.bridge = bridge
|
|
70
|
+
self.keyboardView = CalculatorKeyboardView()
|
|
71
|
+
self.keyboardView!.frame = CGRect(x: 0, y: 0, width: UIScreen.main.bounds.size.width, height: 240 + getbottomInset())
|
|
72
|
+
self.keyboardView!.input = self
|
|
73
|
+
|
|
74
|
+
backedTextInputView.inputView = self.keyboardView
|
|
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
|
+
|
|
90
|
+
}
|
|
91
|
+
func getbottomInset() -> CGFloat {
|
|
92
|
+
let window = UIApplication.shared.windows.first?.rootViewController?.view
|
|
93
|
+
let bottom = (window?.safeAreaInsets.bottom ?? 0) > 0 ? 21 : 0
|
|
94
|
+
return CGFloat(bottom)
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
func keyDidPress(_ key: String) {
|
|
98
|
+
backedTextInputView.insertText(key)
|
|
99
|
+
value += key
|
|
100
|
+
|
|
101
|
+
if let bridge = bridge {
|
|
102
|
+
bridge.eventDispatcher().sendTextEvent(with: .change, reactTag: reactTag, text: value, key: "\(key)", eventCount: 1)
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
func onBackSpace() {
|
|
107
|
+
value = value.dropLast().description
|
|
108
|
+
DispatchQueue.main.async {
|
|
109
|
+
if let range = self.backedTextInputView.selectedTextRange,
|
|
110
|
+
let fromRange = self.backedTextInputView.position(from: range.start, offset: -1),
|
|
111
|
+
let newRange = self.backedTextInputView.textRange(from: fromRange, to: range.start)
|
|
112
|
+
{
|
|
113
|
+
self.backedTextInputView.replace(newRange, withText: "")
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
if let bridge = bridge {
|
|
118
|
+
bridge.eventDispatcher().sendTextEvent(with: .change, reactTag: reactTag, text: value, key: "back", eventCount: 1)
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
func calculateResult() {
|
|
123
|
+
guard let textField = backedTextInputView as? UITextField,
|
|
124
|
+
let text = textField.text?.replacingOccurrences(of: "×", with: "*").replacingOccurrences(of: "÷", with: "/")
|
|
125
|
+
else {
|
|
126
|
+
return
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
let pattern = "^\\s*(-?\\d+(\\.\\d+)?\\s*[-+*/]\\s*)*-?\\d+(\\.\\d+)?\\s*$"
|
|
130
|
+
let regex = try? NSRegularExpression(pattern: pattern)
|
|
131
|
+
let range = NSRange(location: 0, length: text.utf16.count)
|
|
132
|
+
|
|
133
|
+
if regex?.firstMatch(in: text, options: [], range: range) != nil {
|
|
134
|
+
let expression = NSExpression(format: text)
|
|
135
|
+
if let result = expression.expressionValue(with: nil, context: nil) as? NSNumber {
|
|
136
|
+
textField.text = result.stringValue
|
|
137
|
+
value = result.stringValue
|
|
138
|
+
|
|
139
|
+
if let bridge = bridge {
|
|
140
|
+
bridge.eventDispatcher().sendTextEvent(with: .change, reactTag: reactTag, text: value, key: "=", eventCount: 1)
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
else {
|
|
145
|
+
print("Invalid expression")
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
func emitCustomKey() {
|
|
150
|
+
onCustomKeyEvent?([:])
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
func emitKeyPress(_ key: String) {
|
|
154
|
+
onKeyPress?(["key": key])
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
}
|
|
158
|
+
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
|
|
2
|
+
import Foundation
|
|
3
|
+
import UIKit
|
|
4
|
+
|
|
5
|
+
extension UIColor {
|
|
6
|
+
convenience init(hex: String) {
|
|
7
|
+
let hex = hex.trimmingCharacters(in: CharacterSet.alphanumerics.inverted)
|
|
8
|
+
var int = UInt64()
|
|
9
|
+
Scanner(string: hex).scanHexInt64(&int)
|
|
10
|
+
let a, r, g, b: UInt64
|
|
11
|
+
switch hex.count {
|
|
12
|
+
case 3: // RGB (12-bit)
|
|
13
|
+
(a, r, g, b) = (255, (int >> 8) * 17, (int >> 4 & 0xF) * 17, (int & 0xF) * 17)
|
|
14
|
+
case 6: // RGB (24-bit)
|
|
15
|
+
(a, r, g, b) = (255, int >> 16, int >> 8 & 0xFF, int & 0xFF)
|
|
16
|
+
case 8: // ARGB (32-bit)
|
|
17
|
+
(a, r, g, b) = (int >> 24, int >> 16 & 0xFF, int >> 8 & 0xFF, int & 0xFF)
|
|
18
|
+
default:
|
|
19
|
+
(a, r, g, b) = (255, 0, 0, 0)
|
|
20
|
+
}
|
|
21
|
+
self.init(red: CGFloat(r) / 255, green: CGFloat(g) / 255, blue: CGFloat(b) / 255, alpha: CGFloat(a) / 255)
|
|
22
|
+
}
|
|
23
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@momo-kits/calculator-keyboard",
|
|
3
|
-
"version": "0.150.2-beta.
|
|
3
|
+
"version": "0.150.2-beta.22",
|
|
4
4
|
"description": "react native calculator keyboard",
|
|
5
5
|
"main": "./src/index.tsx",
|
|
6
6
|
"files": [
|
|
@@ -48,30 +48,19 @@
|
|
|
48
48
|
"publishConfig": {
|
|
49
49
|
"registry": "https://registry.npmjs.org/"
|
|
50
50
|
},
|
|
51
|
-
"codegenConfig": {
|
|
52
|
-
"name": "CalculatorKeyboardSpecs",
|
|
53
|
-
"type": "components",
|
|
54
|
-
"jsSrcsDir": "src",
|
|
55
|
-
"android": {
|
|
56
|
-
"javaPackageName": "com.calculatorkeyboard"
|
|
57
|
-
},
|
|
58
|
-
"ios": {
|
|
59
|
-
"componentProvider": {
|
|
60
|
-
"NativeInputCalculator": "NativeInputCalculator"
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
|
-
},
|
|
64
51
|
"devDependencies": {
|
|
65
|
-
"
|
|
66
|
-
"react
|
|
52
|
+
"prettier": "^3.0.3",
|
|
53
|
+
"react": "19.0.0",
|
|
54
|
+
"react-native": "0.80.1",
|
|
55
|
+
"react-native-builder-bob": "^0.40.12",
|
|
56
|
+
"release-it": "^17.10.0",
|
|
57
|
+
"turbo": "^1.10.7",
|
|
58
|
+
"typescript": "^5.8.3"
|
|
67
59
|
},
|
|
68
60
|
"peerDependencies": {
|
|
69
61
|
"react": "*",
|
|
70
62
|
"react-native": "*",
|
|
71
63
|
"@momo-kits/foundation": "latest"
|
|
72
64
|
},
|
|
73
|
-
"engines": {
|
|
74
|
-
"node": ">=18.0.0"
|
|
75
|
-
},
|
|
76
65
|
"dependencies": {}
|
|
77
66
|
}
|
|
@@ -5,7 +5,7 @@ folly_compiler_flags = '-DFOLLY_NO_CONFIG -DFOLLY_MOBILE=1 -DFOLLY_USE_LIBCPP=1
|
|
|
5
5
|
|
|
6
6
|
Pod::Spec.new do |s|
|
|
7
7
|
s.name = "react-native-calculator-keyboard"
|
|
8
|
-
s.version =
|
|
8
|
+
s.version = "0.0.1"
|
|
9
9
|
s.summary = package["description"]
|
|
10
10
|
s.homepage = package["homepage"]
|
|
11
11
|
s.license = package["license"]
|
|
@@ -14,10 +14,9 @@ Pod::Spec.new do |s|
|
|
|
14
14
|
s.platforms = { :ios => min_ios_version_supported }
|
|
15
15
|
s.source = { :git => "https://github.com/wem2017/react-native-calculator-keyboard.git/react-native-calculator-keyboard.git", :tag => "#{s.version}" }
|
|
16
16
|
|
|
17
|
-
s.source_files = "ios/**/*.{h,m,mm}"
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
17
|
+
s.source_files = "ios/**/*.{h,m,mm,swift}"
|
|
18
|
+
s.resources = "**/Assets/**/*.{json}"
|
|
19
|
+
|
|
21
20
|
# Use install_modules_dependencies helper to install the dependencies if React Native version >=0.71.0.
|
|
22
21
|
# See https://github.com/facebook/react-native/blob/febf6b7f33fdb4904669f99d795eba4c0f95d7bf/scripts/cocoapods/new_architecture.rb#L79.
|
|
23
22
|
if respond_to?(:install_modules_dependencies, true)
|
package/src/index.tsx
CHANGED
|
@@ -1,31 +1,28 @@
|
|
|
1
|
-
import
|
|
2
|
-
import { useContext } from 'react';
|
|
1
|
+
import React, { useContext } from 'react';
|
|
3
2
|
import { ApplicationContext, Colors } from '@momo-kits/foundation';
|
|
4
3
|
import {
|
|
4
|
+
type ColorValue,
|
|
5
|
+
findNodeHandle,
|
|
5
6
|
type NativeSyntheticEvent,
|
|
7
|
+
processColor,
|
|
8
|
+
requireNativeComponent,
|
|
6
9
|
type TextInputProps,
|
|
7
|
-
|
|
8
|
-
StyleProp,
|
|
9
|
-
TextStyle,
|
|
10
|
+
UIManager,
|
|
10
11
|
} from 'react-native';
|
|
11
|
-
import NativeInputCalculator, {
|
|
12
|
-
TextAttributes,
|
|
13
|
-
} from './InputCalculatorNativeComponent';
|
|
14
|
-
import { Commands } from './InputCalculatorNativeComponent';
|
|
15
12
|
|
|
16
|
-
const
|
|
13
|
+
const NAME = 'RCTInputCalculator';
|
|
14
|
+
const NativeInput = requireNativeComponent<any>(NAME);
|
|
17
15
|
|
|
18
16
|
type KeyPressEvent = { nativeEvent: { key: string } };
|
|
19
17
|
|
|
20
18
|
interface InputCalculatorProps extends TextInputProps {
|
|
21
19
|
text?: string | undefined;
|
|
22
|
-
|
|
20
|
+
keyboardColor?: ColorValue;
|
|
23
21
|
onKeyPress?: (e: KeyPressEvent) => void;
|
|
24
22
|
customKeyText?: string | undefined;
|
|
25
23
|
customKeyBackground?: CustomKeyBackground;
|
|
26
24
|
customKeyState?: CustomKeyState;
|
|
27
25
|
onCustomKeyEvent?: () => void;
|
|
28
|
-
style?: StyleProp<TextStyle>;
|
|
29
26
|
}
|
|
30
27
|
|
|
31
28
|
export type CustomKeyBackground = 'primary' | 'default' | string;
|
|
@@ -35,36 +32,11 @@ export enum CustomKeyState {
|
|
|
35
32
|
Disable = 'disable',
|
|
36
33
|
}
|
|
37
34
|
|
|
38
|
-
export enum Mode {
|
|
39
|
-
NumDefault = 'NumDefault',
|
|
40
|
-
NumWithCTA = 'NumWithCTA',
|
|
41
|
-
}
|
|
42
|
-
|
|
43
35
|
export type InputCalculatorRef = {
|
|
44
36
|
focus: () => void;
|
|
45
37
|
blur: () => void;
|
|
46
38
|
};
|
|
47
39
|
|
|
48
|
-
const omitUndefined = <T extends object>(obj: T): T =>
|
|
49
|
-
Object.fromEntries(
|
|
50
|
-
Object.entries(obj).filter(([, v]) => v !== undefined),
|
|
51
|
-
) as T;
|
|
52
|
-
|
|
53
|
-
function isEmpty(obj: object | undefined | null) {
|
|
54
|
-
return !obj || Object.keys(obj).length === 0;
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
export function styleToTextAttributes(
|
|
58
|
-
style?: StyleProp<TextStyle>,
|
|
59
|
-
): Readonly<TextAttributes> {
|
|
60
|
-
const s = StyleSheet.flatten(style) ?? {};
|
|
61
|
-
const attrs: TextAttributes = {
|
|
62
|
-
fontSize: s.fontSize ?? undefined,
|
|
63
|
-
fontWeight: (s.fontWeight as TextAttributes['fontWeight']) ?? undefined,
|
|
64
|
-
};
|
|
65
|
-
return Object.freeze(omitUndefined(attrs));
|
|
66
|
-
}
|
|
67
|
-
|
|
68
40
|
const InputCalculator = React.forwardRef<
|
|
69
41
|
InputCalculatorRef,
|
|
70
42
|
InputCalculatorProps
|
|
@@ -72,12 +44,11 @@ const InputCalculator = React.forwardRef<
|
|
|
72
44
|
(
|
|
73
45
|
{
|
|
74
46
|
customKeyBackground = 'default',
|
|
75
|
-
|
|
47
|
+
keyboardColor = '',
|
|
76
48
|
customKeyText,
|
|
77
49
|
onKeyPress,
|
|
78
50
|
customKeyState = CustomKeyState.Default,
|
|
79
51
|
onCustomKeyEvent,
|
|
80
|
-
style,
|
|
81
52
|
...props
|
|
82
53
|
},
|
|
83
54
|
ref,
|
|
@@ -95,42 +66,43 @@ const InputCalculator = React.forwardRef<
|
|
|
95
66
|
let keyBackground = Colors.black_06;
|
|
96
67
|
let textKeyColor = Colors.black_20;
|
|
97
68
|
|
|
98
|
-
if (
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
}
|
|
69
|
+
if (customKeyBackground === 'primary') {
|
|
70
|
+
keyBackground = theme.colors.primary;
|
|
71
|
+
textKeyColor = Colors.black_01;
|
|
72
|
+
}
|
|
103
73
|
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
}
|
|
74
|
+
if (customKeyState === CustomKeyState.Disable) {
|
|
75
|
+
keyBackground = theme.colors.background.disable;
|
|
76
|
+
textKeyColor = Colors.black_01;
|
|
108
77
|
}
|
|
109
78
|
|
|
110
79
|
React.useImperativeHandle(ref, () => ({
|
|
111
80
|
focus() {
|
|
112
|
-
|
|
81
|
+
const node = findNodeHandle(nativeRef.current);
|
|
82
|
+
if (!node) return;
|
|
83
|
+
const config = UIManager.getViewManagerConfig(NAME);
|
|
84
|
+
if (config?.Commands?.focus != null) {
|
|
85
|
+
UIManager.dispatchViewManagerCommand(node, config.Commands.focus, []);
|
|
86
|
+
}
|
|
113
87
|
},
|
|
114
88
|
blur() {
|
|
115
|
-
|
|
89
|
+
const node = findNodeHandle(nativeRef.current);
|
|
90
|
+
if (!node) return;
|
|
91
|
+
const config = UIManager.getViewManagerConfig(NAME);
|
|
92
|
+
if (config?.Commands?.blur != null) {
|
|
93
|
+
UIManager.dispatchViewManagerCommand(node, config.Commands.blur, []);
|
|
94
|
+
}
|
|
116
95
|
},
|
|
117
96
|
}));
|
|
118
97
|
|
|
119
|
-
const derivedTA = React.useMemo(
|
|
120
|
-
() => styleToTextAttributes(style),
|
|
121
|
-
[style],
|
|
122
|
-
);
|
|
123
|
-
|
|
124
98
|
return (
|
|
125
99
|
<NativeInput
|
|
126
100
|
{...props}
|
|
127
|
-
style={style}
|
|
128
|
-
textAttributes={isEmpty(derivedTA) ? undefined : derivedTA}
|
|
129
101
|
ref={nativeRef}
|
|
130
102
|
onChange={_onChange}
|
|
131
103
|
onKeyPress={onKeyPress}
|
|
132
104
|
value={text}
|
|
133
|
-
|
|
105
|
+
keybardColor={processColor(keyboardColor)}
|
|
134
106
|
customKeyText={customKeyText}
|
|
135
107
|
customKeyBackground={keyBackground}
|
|
136
108
|
customKeyTextColor={textKeyColor}
|