@react-native-ohos/react-native-text-input-mask 3.1.6-rc.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.
- package/LICENSE +21 -0
- package/README.OpenSource +11 -0
- package/README.md +134 -0
- package/dist/babel.config.d.ts +7 -0
- package/dist/babel.config.js +16 -0
- package/dist/babel.config.js.map +1 -0
- package/dist/index.d.ts +138 -0
- package/dist/index.js +92 -0
- package/dist/index.js.map +1 -0
- package/dist/src/RNNativeTextInputMask.d.ts +129 -0
- package/dist/src/RNNativeTextInputMask.js +8 -0
- package/dist/src/RNNativeTextInputMask.js.map +1 -0
- package/dist/src/index.d.ts +8 -0
- package/dist/src/index.harmony.d.ts +127 -0
- package/dist/src/index.harmony.js +23 -0
- package/dist/src/index.harmony.js.map +1 -0
- package/dist/src/index.js +27 -0
- package/dist/src/index.js.map +1 -0
- package/dist/tsconfig.tsbuildinfo +1 -0
- package/harmony/text_input_mask/BuildProfile.ets +17 -0
- package/harmony/text_input_mask/Index.ets +7 -0
- package/harmony/text_input_mask/build-profile.json5 +31 -0
- package/harmony/text_input_mask/consumer-rules.txt +0 -0
- package/harmony/text_input_mask/hvigorfile.ts +6 -0
- package/harmony/text_input_mask/obfuscation-rules.txt +23 -0
- package/harmony/text_input_mask/oh-package.json5 +11 -0
- package/harmony/text_input_mask/src/main/cpp/CMakeLists.txt +9 -0
- package/harmony/text_input_mask/src/main/cpp/RNTextInputMask.cpp +415 -0
- package/harmony/text_input_mask/src/main/cpp/RNTextInputMask.h +93 -0
- package/harmony/text_input_mask/src/main/cpp/RNTextInputMaskPackage.h +32 -0
- package/harmony/text_input_mask/src/main/cpp/common/Compiler.h +175 -0
- package/harmony/text_input_mask/src/main/cpp/common/FormatError.h +23 -0
- package/harmony/text_input_mask/src/main/cpp/common/FormatSanitizer.h +231 -0
- package/harmony/text_input_mask/src/main/cpp/common/Mask.h +378 -0
- package/harmony/text_input_mask/src/main/cpp/common/RTLMask.h +79 -0
- package/harmony/text_input_mask/src/main/cpp/common/model/AffinityCalculationStrategy.h +58 -0
- package/harmony/text_input_mask/src/main/cpp/common/model/CaretString.h +76 -0
- package/harmony/text_input_mask/src/main/cpp/common/model/CaretStringIterator.h +58 -0
- package/harmony/text_input_mask/src/main/cpp/common/model/Next.h +25 -0
- package/harmony/text_input_mask/src/main/cpp/common/model/Notation.h +25 -0
- package/harmony/text_input_mask/src/main/cpp/common/model/RTLCaretStringIterator.h +23 -0
- package/harmony/text_input_mask/src/main/cpp/common/model/State.h +302 -0
- package/harmony/text_input_mask/src/main/cpp/common/model/common.h +95 -0
- package/harmony/text_input_mask/src/main/ets/RNTextInputMaskPackage.ts +29 -0
- package/harmony/text_input_mask/src/main/ets/RNTextInputMaskTurboModle.ts +33 -0
- package/harmony/text_input_mask/src/main/module.json5 +11 -0
- package/harmony/text_input_mask/src/main/resources/base/element/string.json +8 -0
- package/harmony/text_input_mask/src/main/resources/en_US/element/string.json +8 -0
- package/harmony/text_input_mask/src/main/resources/zh_CN/element/string.json +8 -0
- package/harmony/text_input_mask/ts.ts +8 -0
- package/harmony/text_input_mask.har +0 -0
- package/index.tsx +258 -0
- package/package.json +50 -0
- package/src/RNNativeTextInputMask.ts +143 -0
- package/src/index.harmony.ts +147 -0
- package/src/index.ts +36 -0
|
@@ -0,0 +1,415 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright (c) 2024 Huawei Device Co., Ltd. All rights reserved
|
|
3
|
+
* Use of this source code is governed by a MIT license that can be
|
|
4
|
+
* found in the LICENSE file.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
#include "RNTextInputMask.h"
|
|
8
|
+
#include "RNOH/arkui/TextInputNode.h"
|
|
9
|
+
#include "RNOH/ComponentInstance.h"
|
|
10
|
+
#include "RNOH/RNInstanceCAPI.h"
|
|
11
|
+
#include "RNOHCorePackage/ComponentInstances/TextInputComponentInstance.h"
|
|
12
|
+
#include "common/model/AffinityCalculationStrategy.h"
|
|
13
|
+
|
|
14
|
+
#include <cstdint>
|
|
15
|
+
#include <iostream>
|
|
16
|
+
#include <jsi/jsi.h>
|
|
17
|
+
#include <string>
|
|
18
|
+
#include <thread>
|
|
19
|
+
|
|
20
|
+
using namespace facebook;
|
|
21
|
+
using namespace rnoh;
|
|
22
|
+
using namespace react;
|
|
23
|
+
using namespace TinpMask;
|
|
24
|
+
static constexpr int AVOIDENCE = 1;
|
|
25
|
+
std::unordered_map<std::string, std::shared_ptr<RTLMask>> TinpMask::RTLMask::cache;
|
|
26
|
+
std::unordered_map<std::string, std::shared_ptr<Mask>> TinpMask::Mask::MaskFactory::maskCache;
|
|
27
|
+
void maybeThrow(int32_t status) {
|
|
28
|
+
DLOG(INFO) << "=====text change maybeThrow status: " << status;
|
|
29
|
+
if (status != 0) {
|
|
30
|
+
auto message = std::string("ArkUINode operation failed with status: ") + std::to_string(status);
|
|
31
|
+
LOG(ERROR) << message;
|
|
32
|
+
throw std::runtime_error(std::move(message));
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
void myEventReceiver(ArkUI_NodeEvent *event) {
|
|
37
|
+
int32_t eventId = OH_ArkUI_NodeEvent_GetTargetId(event);
|
|
38
|
+
ArkUI_NodeHandle textNode = OH_ArkUI_NodeEvent_GetNodeHandle(event);
|
|
39
|
+
void *data = NativeNodeApi::getInstance()->getUserData(textNode);
|
|
40
|
+
auto item = NativeNodeApi::getInstance()->getAttribute(textNode, NODE_TEXT_INPUT_TEXT);
|
|
41
|
+
std::string content = item->string;
|
|
42
|
+
UserData *userData = reinterpret_cast<UserData *>(data);
|
|
43
|
+
auto self = userData->instance;
|
|
44
|
+
if (self == nullptr) { return; };
|
|
45
|
+
bool isDelete = userData->lastInputText.size() > content.size();
|
|
46
|
+
bool useAutocomplete = !isDelete ? userData->maskOptions.autocomplete.value() : false;
|
|
47
|
+
bool useAutoskip = isDelete ? userData->maskOptions.autoskip.value() : false;
|
|
48
|
+
|
|
49
|
+
// onChange 事件
|
|
50
|
+
if (eventId == 110) {
|
|
51
|
+
std::shared_ptr<CaretString::CaretGravity> caretGravity =
|
|
52
|
+
isDelete ? std::make_shared<CaretString::CaretGravity>(CaretString::Backward(useAutoskip))
|
|
53
|
+
: std::make_shared<CaretString::CaretGravity>(CaretString::Forward(useAutocomplete));
|
|
54
|
+
CaretString text(content, content.length(), caretGravity);
|
|
55
|
+
try {
|
|
56
|
+
auto maskObj = self->pickMask(text, userData->maskOptions, userData->primaryFormat);
|
|
57
|
+
auto result = maskObj->apply(text);
|
|
58
|
+
std::string resultString = result.formattedText.string;
|
|
59
|
+
DLOG(INFO) << "mask result complete: " << result.complete;
|
|
60
|
+
userData->lastInputText = resultString;
|
|
61
|
+
std::string finalString = isDelete ? content : resultString;
|
|
62
|
+
ArkUI_AttributeItem item{.string = finalString.c_str()};
|
|
63
|
+
userData->lastInputText = finalString;
|
|
64
|
+
maybeThrow(NativeNodeApi::getInstance()->setAttribute(userData->data, NODE_TEXT_INPUT_TEXT, &item));
|
|
65
|
+
} catch (FormatError e) {
|
|
66
|
+
DLOG(ERROR) << " mask complier error " << e.what();
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
// onFocus 事件
|
|
70
|
+
if (eventId == 111) {
|
|
71
|
+
if (userData->maskOptions.autocomplete.value()) {
|
|
72
|
+
std::string text = "";
|
|
73
|
+
text += content;
|
|
74
|
+
try {
|
|
75
|
+
CaretString string(text, text.length(),
|
|
76
|
+
std::make_shared<CaretString::Forward>(userData->maskOptions.autocomplete.value()));
|
|
77
|
+
auto maskObj = self->pickMask(string, userData->maskOptions, userData->primaryFormat);
|
|
78
|
+
std::string resultString = maskObj->apply(string).formattedText.string;
|
|
79
|
+
ArkUI_AttributeItem item{.string = resultString.c_str()};
|
|
80
|
+
maybeThrow(NativeNodeApi::getInstance()->setAttribute(userData->data, NODE_TEXT_INPUT_TEXT, &item));
|
|
81
|
+
} catch (FormatError e) {
|
|
82
|
+
DLOG(ERROR) << " mask complier error " << e.what();
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
// 计算亲和度
|
|
89
|
+
int calculateAffinity(Mask mask, const CaretString &text, std::string affinityCalculationStrategy) {
|
|
90
|
+
AffinityCalculationStrategy strategy;
|
|
91
|
+
if (affinityCalculationStrategy == "WHOLE_STRING") {
|
|
92
|
+
strategy = AffinityCalculationStrategy::WHOLE_STRING;
|
|
93
|
+
} else if (affinityCalculationStrategy == "PREFIX") {
|
|
94
|
+
strategy = AffinityCalculationStrategy::PREFIX;
|
|
95
|
+
} else if (affinityCalculationStrategy == "CAPACITY") {
|
|
96
|
+
strategy = AffinityCalculationStrategy::CAPACITY;
|
|
97
|
+
} else {
|
|
98
|
+
strategy = AffinityCalculationStrategy::EXTRACTED_VALUE_CAPACITY;
|
|
99
|
+
}
|
|
100
|
+
int affinity = AffinityCalculator::calculateAffinityOfMask(strategy, mask, text);
|
|
101
|
+
|
|
102
|
+
return affinity;
|
|
103
|
+
}
|
|
104
|
+
// 获取或创建 Mask
|
|
105
|
+
std::shared_ptr<Mask> maskGetOrCreate(const std::string &format, const std::vector<Notation> &customNotations,
|
|
106
|
+
bool rightToLeft) {
|
|
107
|
+
if (rightToLeft) {
|
|
108
|
+
return RTLMask::getOrCreate(format, customNotations);
|
|
109
|
+
} else {
|
|
110
|
+
return Mask::MaskFactory::getOrCreate(format, customNotations);
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
std::shared_ptr<Mask> RNTextInputMask::pickMask(const CaretString &text, MaskOptions maskOptions,
|
|
115
|
+
std::string primaryMask) {
|
|
116
|
+
// 如果 affineFormats 为空,直接返回 primaryMask
|
|
117
|
+
if (maskOptions.affineFormats->size() <= 0) {
|
|
118
|
+
auto mask = maskGetOrCreate(primaryMask, maskOptions.customNotations.value(), maskOptions.rightToLeft.value());
|
|
119
|
+
int affinity = calculateAffinity(*mask, text, maskOptions.affinityCalculationStrategy.value());
|
|
120
|
+
DLOG(INFO) << " ======= pickMask calculateAffinity value: " << affinity
|
|
121
|
+
<< "\n affinityCalculationStrategy: " << maskOptions.affinityCalculationStrategy.value();
|
|
122
|
+
return mask;
|
|
123
|
+
}
|
|
124
|
+
// 定义 MaskAffinity 结构体,用于存储 Mask 和相应的亲和度
|
|
125
|
+
struct MaskAffinity {
|
|
126
|
+
Mask mask; // Mask 对象
|
|
127
|
+
int affinity; // 亲和度
|
|
128
|
+
MaskAffinity(const Mask &m, int a) : mask(m), affinity(a) {}
|
|
129
|
+
};
|
|
130
|
+
|
|
131
|
+
// 计算 primaryMask 的亲和度
|
|
132
|
+
int primaryAffinity = calculateAffinity(primaryMask, text, maskOptions.affinityCalculationStrategy.value());
|
|
133
|
+
DLOG(INFO) << " ======= pickMask calculateAffinity value: " << primaryAffinity << " \n mask: " << primaryMask
|
|
134
|
+
<< "\n affinityCalculationStrategy: " << maskOptions.affinityCalculationStrategy.value();
|
|
135
|
+
// 存储所有 mask 和亲和度的列表
|
|
136
|
+
std::vector<MaskAffinity> masksAndAffinities;
|
|
137
|
+
|
|
138
|
+
// 遍历 affineFormats 以获取每个格式的 Mask 和亲和度
|
|
139
|
+
for (const auto &format : *maskOptions.affineFormats) {
|
|
140
|
+
std::shared_ptr<Mask> mask =
|
|
141
|
+
maskGetOrCreate(format, maskOptions.customNotations.value(), maskOptions.rightToLeft.value());
|
|
142
|
+
int affinity = calculateAffinity(*mask, text, maskOptions.affinityCalculationStrategy.value());
|
|
143
|
+
DLOG(INFO) << " ======= pickMask calculateAffinity value: " << affinity << "\n affineFormat: " << format
|
|
144
|
+
<< "\n affinityCalculationStrategy: " << maskOptions.affinityCalculationStrategy.value();
|
|
145
|
+
masksAndAffinities.emplace_back(*mask, affinity);
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
// 按亲和度降序排序
|
|
149
|
+
std::sort(masksAndAffinities.begin(), masksAndAffinities.end(),
|
|
150
|
+
[](const MaskAffinity &a, const MaskAffinity &b) { return a.affinity > b.affinity; });
|
|
151
|
+
|
|
152
|
+
// 寻找插入位置
|
|
153
|
+
int insertIndex = -1;
|
|
154
|
+
for (size_t index = 0; index < masksAndAffinities.size(); ++index) {
|
|
155
|
+
if (primaryAffinity >= masksAndAffinities[index].affinity) {
|
|
156
|
+
insertIndex = static_cast<int>(index);
|
|
157
|
+
break;
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
// 插入 primaryMask
|
|
162
|
+
if (insertIndex >= 0) {
|
|
163
|
+
masksAndAffinities.insert(masksAndAffinities.begin() + insertIndex, MaskAffinity(primaryMask, primaryAffinity));
|
|
164
|
+
} else {
|
|
165
|
+
masksAndAffinities.emplace_back(primaryMask, primaryAffinity);
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
// 返回亲和度最高的 Mask
|
|
169
|
+
return std::make_shared<Mask>(masksAndAffinities.front().mask);
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
void RNTextInputMask::setMask(int reactNode, std::string primaryFormat, MaskOptions maskOptions) {
|
|
173
|
+
auto task = [this, reactNode, primaryFormat, maskOptions] {
|
|
174
|
+
auto weakInstance = m_ctx.instance;
|
|
175
|
+
auto instance = weakInstance.lock();
|
|
176
|
+
auto instanceCAPI = std::dynamic_pointer_cast<RNInstanceCAPI>(instance);
|
|
177
|
+
if (!instanceCAPI) {
|
|
178
|
+
return;
|
|
179
|
+
}
|
|
180
|
+
auto componentInstance = instanceCAPI->findComponentInstanceByTag(reactNode);
|
|
181
|
+
if (!componentInstance) {
|
|
182
|
+
return;
|
|
183
|
+
}
|
|
184
|
+
auto input = std::dynamic_pointer_cast<TextInputComponentInstance>(componentInstance);
|
|
185
|
+
if (!input) {
|
|
186
|
+
throw std::runtime_error("find ComponentInstance failed,check the reactNode is Valid ");
|
|
187
|
+
}
|
|
188
|
+
ArkUINode &node = input->getLocalRootArkUINode();
|
|
189
|
+
TextInputNode *textInputNode = dynamic_cast<TextInputNode *>(&node);
|
|
190
|
+
UserData *userData = new UserData({.data = textInputNode->getArkUINodeHandle(),
|
|
191
|
+
.maskOptions = maskOptions,
|
|
192
|
+
.primaryFormat = primaryFormat,
|
|
193
|
+
.node = reactNode,
|
|
194
|
+
.instance = this});
|
|
195
|
+
NativeNodeApi::getInstance()->registerNodeEvent(textInputNode->getArkUINodeHandle(), NODE_TEXT_INPUT_ON_CHANGE,
|
|
196
|
+
110, textInputNode);
|
|
197
|
+
NativeNodeApi::getInstance()->registerNodeEvent(textInputNode->getArkUINodeHandle(), NODE_ON_FOCUS, 111,
|
|
198
|
+
textInputNode);
|
|
199
|
+
this->m_userDatas.insert(userData);
|
|
200
|
+
NativeNodeApi::getInstance()->setUserData(textInputNode->getArkUINodeHandle(), userData);
|
|
201
|
+
NativeNodeApi::getInstance()->addNodeEventReceiver(textInputNode->getArkUINodeHandle(), myEventReceiver);
|
|
202
|
+
};
|
|
203
|
+
this->m_ctx.taskExecutor->runTask(TaskThread::MAIN, std::move(task));
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
std::string getString(std::string maskValue, std::string value, bool autocomplete, bool isMask) {
|
|
207
|
+
|
|
208
|
+
auto maskObj = Mask::MaskFactory::getOrCreate(maskValue, {});
|
|
209
|
+
CaretString text(value, value.length(), std::make_shared<CaretString::Forward>(autocomplete));
|
|
210
|
+
auto r = maskObj->apply(text);
|
|
211
|
+
std::string result;
|
|
212
|
+
if (isMask) {
|
|
213
|
+
result = r.formattedText.string;
|
|
214
|
+
} else {
|
|
215
|
+
result = r.extractedValue;
|
|
216
|
+
}
|
|
217
|
+
return result;
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
std::string getString(std::string maskValue, std::string value, bool autocomplete, bool isMask, bool rightToLeft) {
|
|
221
|
+
std::shared_ptr<Mask> maskObj;
|
|
222
|
+
if (rightToLeft) {
|
|
223
|
+
maskObj = RTLMask::getOrCreate(maskValue, {});
|
|
224
|
+
} else {
|
|
225
|
+
maskObj = Mask::MaskFactory::getOrCreate(maskValue, {});
|
|
226
|
+
}
|
|
227
|
+
CaretString text(value, value.length(), std::make_shared<CaretString::Forward>(autocomplete));
|
|
228
|
+
auto r = maskObj->apply(text);
|
|
229
|
+
std::string result;
|
|
230
|
+
if (isMask) {
|
|
231
|
+
result = r.formattedText.string;
|
|
232
|
+
} else {
|
|
233
|
+
result = r.extractedValue;
|
|
234
|
+
}
|
|
235
|
+
return result;
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
static jsi::Value __hostFunction_RNTextInputMask_unmask(jsi::Runtime &rt, react::TurboModule &turboModule,
|
|
239
|
+
const jsi::Value *args, size_t count) {
|
|
240
|
+
std::string maskValue = args[0].getString(rt).utf8(rt);
|
|
241
|
+
std::string value = args[1].getString(rt).utf8(rt);
|
|
242
|
+
bool autocomplete = args[2].getBool();
|
|
243
|
+
return createPromiseAsJSIValue(
|
|
244
|
+
rt, [maskValue, value, autocomplete](jsi::Runtime &rt2, std::shared_ptr<facebook::react::Promise> promise) {
|
|
245
|
+
try {
|
|
246
|
+
auto start = std::chrono::high_resolution_clock::now();
|
|
247
|
+
std::string result = getString(maskValue, value, autocomplete, 0);
|
|
248
|
+
promise->resolve(jsi::String::createFromUtf8(rt2, result));
|
|
249
|
+
// 获取结束时间点
|
|
250
|
+
auto end = std::chrono::high_resolution_clock::now();
|
|
251
|
+
// 计算延迟
|
|
252
|
+
std::chrono::duration<double, std::milli> latency = end - start;
|
|
253
|
+
DLOG(INFO) << "=======unmask 响应时长: " << latency.count() << " 毫秒" << std::endl;
|
|
254
|
+
} catch (FormatError e) {
|
|
255
|
+
promise->reject(e.what());
|
|
256
|
+
}
|
|
257
|
+
});
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
static jsi::Value __hostFunction_RNTextInputMask_unmaskWithRightToLeft(jsi::Runtime &rt, react::TurboModule &turboModule,
|
|
261
|
+
const jsi::Value *args, size_t count) {
|
|
262
|
+
std::string maskValue = args[0].getString(rt).utf8(rt);
|
|
263
|
+
std::string value = args[1].getString(rt).utf8(rt);
|
|
264
|
+
bool autocomplete = args[2].getBool();
|
|
265
|
+
bool rightToLeft = args[3].getBool();
|
|
266
|
+
return createPromiseAsJSIValue(
|
|
267
|
+
rt, [maskValue, value, autocomplete, rightToLeft](jsi::Runtime &rt2, std::shared_ptr<facebook::react::Promise> promise) {
|
|
268
|
+
try {
|
|
269
|
+
auto start = std::chrono::high_resolution_clock::now();
|
|
270
|
+
std::string result = getString(maskValue, value, autocomplete, 0, rightToLeft);
|
|
271
|
+
promise->resolve(jsi::String::createFromUtf8(rt2, result));
|
|
272
|
+
// 获取结束时间点
|
|
273
|
+
auto end = std::chrono::high_resolution_clock::now();
|
|
274
|
+
// 计算延迟
|
|
275
|
+
std::chrono::duration<double, std::milli> latency = end - start;
|
|
276
|
+
DLOG(INFO) << "=======unmask 响应时长: " << latency.count() << " 毫秒" << std::endl;
|
|
277
|
+
} catch (FormatError e) {
|
|
278
|
+
promise->reject(e.what());
|
|
279
|
+
}
|
|
280
|
+
});
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
static jsi::Value __hostFunction_RNTextInputMask_mask(jsi::Runtime &rt, react::TurboModule &turboModule,
|
|
284
|
+
const jsi::Value *args, size_t count) {
|
|
285
|
+
std::string maskValue = args[0].getString(rt).utf8(rt);
|
|
286
|
+
std::string value = args[1].getString(rt).utf8(rt);
|
|
287
|
+
bool autocomplete = args[2].getBool();
|
|
288
|
+
return createPromiseAsJSIValue(
|
|
289
|
+
rt, [maskValue, value, autocomplete](jsi::Runtime &rt2, std::shared_ptr<facebook::react::Promise> promise) {
|
|
290
|
+
try {
|
|
291
|
+
auto start = std::chrono::high_resolution_clock::now();
|
|
292
|
+
std::string result = getString(maskValue, value, autocomplete, 1);
|
|
293
|
+
promise->resolve(jsi::String::createFromUtf8(rt2, result));
|
|
294
|
+
// 获取结束时间点
|
|
295
|
+
auto end = std::chrono::high_resolution_clock::now();
|
|
296
|
+
// 计算延迟
|
|
297
|
+
std::chrono::duration<double, std::milli> latency = end - start;
|
|
298
|
+
DLOG(INFO) << "=======mask 响应时长: " << latency.count() << " 毫秒" << std::endl;
|
|
299
|
+
} catch (FormatError e) {
|
|
300
|
+
promise->reject(e.what());
|
|
301
|
+
}
|
|
302
|
+
});
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
static jsi::Value __hostFunction_RNTextInputMask_setMask(jsi::Runtime &rt, react::TurboModule &turboModule,
|
|
306
|
+
const jsi::Value *args, size_t count) {
|
|
307
|
+
|
|
308
|
+
auto turbo = static_cast<RNTextInputMask *>(&turboModule);
|
|
309
|
+
if (turbo->grt == nullptr) {
|
|
310
|
+
turbo->grt = &rt;
|
|
311
|
+
}
|
|
312
|
+
int reactNode = args[0].getNumber();
|
|
313
|
+
std::string primaryFormat = args[1].getString(rt).utf8(rt);
|
|
314
|
+
jsi::Object obj = args[2].asObject(rt);
|
|
315
|
+
std::vector<std::string> affineFormatsValues; // 用于存储数组的值
|
|
316
|
+
if (obj.hasProperty(rt, "affineFormats") && !obj.getProperty(rt, "affineFormats").isUndefined()) {
|
|
317
|
+
jsi::Object affineFormats = obj.getProperty(rt, "affineFormats").asObject(rt);
|
|
318
|
+
// 确保它是一个数组
|
|
319
|
+
if (affineFormats.isArray(rt)) {
|
|
320
|
+
// 获取数组的长度
|
|
321
|
+
jsi::Array arrayAffineFormats = affineFormats.asArray(rt);
|
|
322
|
+
size_t length = arrayAffineFormats.size(rt);
|
|
323
|
+
// 遍历数组
|
|
324
|
+
for (size_t i = 0; i < length; ++i) {
|
|
325
|
+
// 获取数组元素
|
|
326
|
+
jsi::Value value = arrayAffineFormats.getValueAtIndex(rt, i);
|
|
327
|
+
std::cout << "===affineFormats index" + std::to_string(i) << "--" << value.getString(rt).utf8(rt)
|
|
328
|
+
<< std::endl;
|
|
329
|
+
affineFormatsValues.push_back(value.getString(rt).utf8(rt));
|
|
330
|
+
}
|
|
331
|
+
} else {
|
|
332
|
+
std::cerr << "====The property 'myArray' is not an array." << std::endl;
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
std::vector<Notation> customNotationsValues;
|
|
336
|
+
if (obj.hasProperty(rt, "customNotations") && !obj.getProperty(rt, "customNotations").isUndefined()) {
|
|
337
|
+
jsi::Object customNotations = obj.getProperty(rt, "customNotations").asObject(rt);
|
|
338
|
+
// 确保它是一个数组
|
|
339
|
+
if (customNotations.isArray(rt)) {
|
|
340
|
+
// 获取数组的长度
|
|
341
|
+
jsi::Array arrayCustomNotations = customNotations.asArray(rt);
|
|
342
|
+
size_t length = arrayCustomNotations.size(rt);
|
|
343
|
+
// 遍历数组
|
|
344
|
+
for (size_t i = 0; i < length; ++i) {
|
|
345
|
+
// 获取数组元素
|
|
346
|
+
jsi::Object value = arrayCustomNotations.getValueAtIndex(rt, i).asObject(rt);
|
|
347
|
+
std::string character;
|
|
348
|
+
if (value.hasProperty(rt, "character")) {
|
|
349
|
+
character = value.getProperty(rt, "character").getString(rt).utf8(rt);
|
|
350
|
+
}
|
|
351
|
+
std::string characterSet;
|
|
352
|
+
if (value.hasProperty(rt, "character")) {
|
|
353
|
+
characterSet = value.getProperty(rt, "characterSet").getString(rt).utf8(rt);
|
|
354
|
+
}
|
|
355
|
+
bool isOptional;
|
|
356
|
+
if (value.hasProperty(rt, "isOptional")) {
|
|
357
|
+
isOptional = value.getProperty(rt, "isOptional").getBool();
|
|
358
|
+
}
|
|
359
|
+
Notation notation(character[0], characterSet, isOptional);
|
|
360
|
+
customNotationsValues.push_back(notation);
|
|
361
|
+
}
|
|
362
|
+
} else {
|
|
363
|
+
std::cerr << "====The property 'myArray' is not an array." << std::endl;
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
|
|
368
|
+
std::string affinityCalculationStrategy;
|
|
369
|
+
if (obj.hasProperty(rt, "affinityCalculationStrategy") &&
|
|
370
|
+
!obj.getProperty(rt, "affinityCalculationStrategy").isUndefined()) {
|
|
371
|
+
affinityCalculationStrategy = obj.getProperty(rt, "affinityCalculationStrategy").asString(rt).utf8(rt);
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
bool autocomplete = true;
|
|
375
|
+
if (obj.hasProperty(rt, "autocomplete") && !obj.getProperty(rt, "autocomplete").isUndefined()) {
|
|
376
|
+
autocomplete = obj.getProperty(rt, "autocomplete").asBool();
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
bool autoskip = false;
|
|
380
|
+
if (obj.hasProperty(rt, "autoskip") && !obj.getProperty(rt, "autoskip").isUndefined()) {
|
|
381
|
+
autoskip = obj.getProperty(rt, "autoskip").asBool();
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
bool rightToLeft = false;
|
|
385
|
+
if (obj.hasProperty(rt, "rightToLeft") && !obj.getProperty(rt, "rightToLeft").isUndefined()) {
|
|
386
|
+
rightToLeft = obj.getProperty(rt, "rightToLeft").asBool();
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
auto maskOptions = new MaskOptions(affineFormatsValues, customNotationsValues, affinityCalculationStrategy,
|
|
390
|
+
autocomplete, autoskip, rightToLeft);
|
|
391
|
+
static_cast<RNTextInputMask *>(&turboModule)->setMask(reactNode, primaryFormat, *maskOptions);
|
|
392
|
+
return jsi::Value::undefined();
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
RNTextInputMask::RNTextInputMask(const ArkTSTurboModule::Context ctx, const std::string name)
|
|
396
|
+
: ArkTSTurboModule(ctx, name) {
|
|
397
|
+
methodMap_["setMask"] = MethodMetadata{3, __hostFunction_RNTextInputMask_setMask};
|
|
398
|
+
methodMap_["mask"] = MethodMetadata{3, __hostFunction_RNTextInputMask_mask};
|
|
399
|
+
methodMap_["unmask"] = MethodMetadata{3, __hostFunction_RNTextInputMask_unmask};
|
|
400
|
+
methodMap_["unmaskWithRightToLeft"] = MethodMetadata{4, __hostFunction_RNTextInputMask_unmaskWithRightToLeft};
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
RNTextInputMask::~RNTextInputMask() {
|
|
404
|
+
for (auto userData : m_userDatas) {
|
|
405
|
+
if (userData != nullptr) {
|
|
406
|
+
NativeNodeApi::getInstance()->unregisterNodeEvent(userData->data, NODE_TEXT_INPUT_ON_CHANGE);
|
|
407
|
+
NativeNodeApi::getInstance()->unregisterNodeEvent(userData->data, NODE_ON_FOCUS);
|
|
408
|
+
NativeNodeApi::getInstance()->removeNodeEventReceiver(userData->data, myEventReceiver);
|
|
409
|
+
delete userData;
|
|
410
|
+
userData = nullptr;
|
|
411
|
+
}
|
|
412
|
+
}
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
// namespace rnoh
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright (c) 2024 Huawei Device Co., Ltd. All rights reserved
|
|
3
|
+
* Use of this source code is governed by a MIT license that can be
|
|
4
|
+
* found in the LICENSE file.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* This code was generated by "react-native codegen-harmony"
|
|
9
|
+
*
|
|
10
|
+
* Do not edit this file as changes may cause incorrect behavior and will be
|
|
11
|
+
* lost once the code is regenerated.
|
|
12
|
+
*
|
|
13
|
+
* @generatorVersion: 1
|
|
14
|
+
*/
|
|
15
|
+
#pragma once
|
|
16
|
+
|
|
17
|
+
#include "RNOH/ArkTSTurboModule.h"
|
|
18
|
+
#include "RNOH/arkui/NativeNodeApi.h"
|
|
19
|
+
#include "RNOH/arkui/TextInputNode.h"
|
|
20
|
+
#include "common/model/Notation.h"
|
|
21
|
+
#include "common/RTLMask.h"
|
|
22
|
+
#include "common/model/AffinityCalculationStrategy.h"
|
|
23
|
+
using namespace rnoh;
|
|
24
|
+
using namespace facebook;
|
|
25
|
+
using namespace TinpMask;
|
|
26
|
+
|
|
27
|
+
void myEventReceiver(ArkUI_NodeEvent *event);
|
|
28
|
+
|
|
29
|
+
namespace rnoh {
|
|
30
|
+
|
|
31
|
+
class RNTextInputMask;
|
|
32
|
+
|
|
33
|
+
struct MaskParams {
|
|
34
|
+
std::string mask; // mask的格式
|
|
35
|
+
std::string value; // 待mask的值
|
|
36
|
+
std::optional<bool> autocomplete; // 布尔值,表示是否自动完成
|
|
37
|
+
MaskParams() = default;
|
|
38
|
+
MaskParams(const std::string &m, const std::string &v, std::optional<bool> autoComplete = std::nullopt)
|
|
39
|
+
: mask(m), value(v), autocomplete(autoComplete) {} // 构造函数
|
|
40
|
+
MaskParams &operator=(const MaskParams &input) {
|
|
41
|
+
this->mask = input.mask;
|
|
42
|
+
this->value = input.value;
|
|
43
|
+
this->autocomplete = input.autocomplete;
|
|
44
|
+
return *this;
|
|
45
|
+
}
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
struct MaskOptions {
|
|
49
|
+
std::optional<std::vector<std::string>> affineFormats; // 可选的字符串数组
|
|
50
|
+
std::optional<std::vector<Notation>> customNotations; // 可选的 Notation 数组
|
|
51
|
+
std::optional<std::string> affinityCalculationStrategy; // 可选字符串
|
|
52
|
+
std::optional<bool> autocomplete; // 可选布尔值
|
|
53
|
+
std::optional<bool> autoskip; // 可选布尔值
|
|
54
|
+
std::optional<bool> rightToLeft; // 可选布尔值
|
|
55
|
+
|
|
56
|
+
MaskOptions()
|
|
57
|
+
: affineFormats(std::vector<std::string>()), customNotations(std::vector<Notation>()),
|
|
58
|
+
affinityCalculationStrategy(std::nullopt), autocomplete(true), autoskip(false), rightToLeft(false) {}
|
|
59
|
+
MaskOptions(const std::vector<std::string> &formats, const std::vector<Notation> ¬ations,
|
|
60
|
+
const std::string &strategy, bool autoComp, bool autoSkip, bool rtl)
|
|
61
|
+
: affineFormats(formats), customNotations(notations),
|
|
62
|
+
affinityCalculationStrategy(strategy.empty() ? std::make_optional("WHOLE_STRING") : std::make_optional(strategy)),
|
|
63
|
+
autocomplete(autoComp), autoskip(autoSkip), rightToLeft(rtl) {}
|
|
64
|
+
};
|
|
65
|
+
|
|
66
|
+
typedef struct {
|
|
67
|
+
ArkUI_NodeHandle data;
|
|
68
|
+
MaskOptions maskOptions;
|
|
69
|
+
std::string primaryFormat;
|
|
70
|
+
int node;
|
|
71
|
+
std::string lastInputText;
|
|
72
|
+
RNTextInputMask *instance;
|
|
73
|
+
} UserData;
|
|
74
|
+
|
|
75
|
+
class JSI_EXPORT RNTextInputMask : public ArkTSTurboModule {
|
|
76
|
+
public:
|
|
77
|
+
RNTextInputMask(const ArkTSTurboModule::Context ctx, const std::string name);
|
|
78
|
+
void setMask(int reactNode, std::string primaryFormat, MaskOptions options);
|
|
79
|
+
jsi::Value mask(std::string mask, std::string value, bool autocomplete);
|
|
80
|
+
jsi::Value unmask(std::string mask, std::string value, bool autocomplete);
|
|
81
|
+
jsi::Value unmaskWithRightToLeft(std::string mask, std::string value, bool autocomplete, bool rightToLeft);
|
|
82
|
+
jsi::Runtime *grt = nullptr;
|
|
83
|
+
std::shared_ptr<Mask> pickMask(const CaretString &text, MaskOptions maskOptions, std::string primaryMask);
|
|
84
|
+
|
|
85
|
+
// 释放资源
|
|
86
|
+
~RNTextInputMask();
|
|
87
|
+
|
|
88
|
+
private:
|
|
89
|
+
std::unordered_set<UserData *> m_userDatas;
|
|
90
|
+
};
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
} // namespace rnoh
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright (c) 2024 Huawei Device Co., Ltd. All rights reserved
|
|
3
|
+
* Use of this source code is governed by a MIT license that can be
|
|
4
|
+
* found in the LICENSE file.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
#pragma once
|
|
8
|
+
#include "RNOH/Package.h"
|
|
9
|
+
#include "RNTextInputMask.h"
|
|
10
|
+
|
|
11
|
+
using namespace rnoh;
|
|
12
|
+
using namespace facebook;
|
|
13
|
+
namespace rnoh {
|
|
14
|
+
class RNTextInputMaskTurboModuleFactoryDelegate : public TurboModuleFactoryDelegate {
|
|
15
|
+
public:
|
|
16
|
+
SharedTurboModule createTurboModule(Context ctx, const std::string &name) const override {
|
|
17
|
+
if (name == "RNTextInputMask") {
|
|
18
|
+
return std::make_shared<RNTextInputMask>(ctx, name);
|
|
19
|
+
}
|
|
20
|
+
return nullptr;
|
|
21
|
+
}
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
class RNTextInputMaskPackage : public Package {
|
|
25
|
+
public:
|
|
26
|
+
RNTextInputMaskPackage(Package::Context ctx) : Package(ctx) {}
|
|
27
|
+
std::unique_ptr<TurboModuleFactoryDelegate> createTurboModuleFactoryDelegate() override {
|
|
28
|
+
return std::make_unique<RNTextInputMaskTurboModuleFactoryDelegate>();
|
|
29
|
+
}
|
|
30
|
+
// std::vector<ArkTSMessageHandler::Shared> createArkTSMessageHandlers() override;
|
|
31
|
+
};
|
|
32
|
+
} // namespace rnoh
|