text-input-guard 1.0.0 → 1.0.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/dist/cjs/text-input-guard.cjs +62 -11
- package/dist/cjs/text-input-guard.min.cjs +1 -1
- package/dist/esm/text-input-guard.js +62 -11
- package/dist/esm/text-input-guard.min.js +1 -1
- package/dist/umd/text-input-guard.js +62 -11
- package/dist/umd/text-input-guard.min.js +1 -1
- package/package.json +1 -1
|
@@ -112,11 +112,16 @@ class SwapState {
|
|
|
112
112
|
|
|
113
113
|
const UI_ATTRS = [
|
|
114
114
|
"placeholder",
|
|
115
|
+
"list",
|
|
115
116
|
"inputmode",
|
|
116
117
|
"autocomplete",
|
|
118
|
+
"autocapitalize",
|
|
119
|
+
"autocorrect",
|
|
117
120
|
"minlength",
|
|
118
121
|
"maxlength",
|
|
122
|
+
"size",
|
|
119
123
|
"pattern",
|
|
124
|
+
"dir",
|
|
120
125
|
"title",
|
|
121
126
|
"tabindex",
|
|
122
127
|
"style",
|
|
@@ -148,6 +153,7 @@ class SwapState {
|
|
|
148
153
|
|
|
149
154
|
for (const [k, v] of Object.entries(input.dataset)) {
|
|
150
155
|
if (k.startsWith("tig")) { continue; }
|
|
156
|
+
if (v == null) { continue; }
|
|
151
157
|
this.originalDataset[k] = v;
|
|
152
158
|
}
|
|
153
159
|
}
|
|
@@ -568,7 +574,7 @@ class InputGuard {
|
|
|
568
574
|
|
|
569
575
|
/**
|
|
570
576
|
* attach時に登録されたバリデーション結果コールバック
|
|
571
|
-
* @type {(result: ValidateResult) => void | undefined}
|
|
577
|
+
* @type {((result: ValidateResult) => void) | undefined}
|
|
572
578
|
*/
|
|
573
579
|
this.onValidate = options.onValidate;
|
|
574
580
|
|
|
@@ -729,6 +735,11 @@ class InputGuard {
|
|
|
729
735
|
*/
|
|
730
736
|
this.beforeInputSnapshot = null;
|
|
731
737
|
|
|
738
|
+
/**
|
|
739
|
+
* onBeforeInput イベントが発生したか否か
|
|
740
|
+
*/
|
|
741
|
+
this.existBeforeInputEvent = false;
|
|
742
|
+
|
|
732
743
|
/**
|
|
733
744
|
* ルールからのrevert要求
|
|
734
745
|
* @type {RevertRequest|null}
|
|
@@ -746,7 +757,7 @@ class InputGuard {
|
|
|
746
757
|
this.applySeparateValue();
|
|
747
758
|
this.bindEvents();
|
|
748
759
|
// 初期値を評価
|
|
749
|
-
this.
|
|
760
|
+
this.evaluateCommit();
|
|
750
761
|
}
|
|
751
762
|
|
|
752
763
|
/**
|
|
@@ -985,12 +996,12 @@ class InputGuard {
|
|
|
985
996
|
*/
|
|
986
997
|
createCtx({ useSnapshot = true } = {}) {
|
|
987
998
|
const snap = useSnapshot ? this.beforeInputSnapshot : null;
|
|
988
|
-
|
|
989
|
-
|
|
999
|
+
let inputType = snap?.inputType ?? "";
|
|
1000
|
+
let insertedText = snap?.insertedText ?? "";
|
|
990
1001
|
|
|
991
1002
|
// 受理済み(正規化済み)の全文を「今回の編集の基準」として使う
|
|
992
1003
|
// display.value はブラウザ側の編集結果が混ざるので、差分再構成の基準にはしない
|
|
993
|
-
|
|
1004
|
+
let beforeText = this.lastAcceptedValue ?? "";
|
|
994
1005
|
|
|
995
1006
|
// selection は2系統ある:
|
|
996
1007
|
// - snapSel: beforeinput 時点で取得した selection(今回の編集の基準点になり得る)
|
|
@@ -1026,6 +1037,38 @@ class InputGuard {
|
|
|
1026
1037
|
baseSel = lastSel;
|
|
1027
1038
|
}
|
|
1028
1039
|
|
|
1040
|
+
// beforeinput がない環境では、差分再構成の基準が「前回の受理値」しかないため、そこから今回の編集内容を推測する必要がある。
|
|
1041
|
+
if (beforeText.length === 0 || !this.existBeforeInputEvent) {
|
|
1042
|
+
const display = /** @type {HTMLInputElement|HTMLTextAreaElement} */ (this.displayElement);
|
|
1043
|
+
const current = display.value;
|
|
1044
|
+
// 前回の値がとれないものの、何かしら入力情報がある状態
|
|
1045
|
+
if (current.length > 0) {
|
|
1046
|
+
// 文字列の先頭が前回の受理値と同じなら、末尾に何かしら入力されたと考えられる(オートコンプリート等)
|
|
1047
|
+
if (current.toLocaleLowerCase().startsWith(beforeText.toLocaleLowerCase())) {
|
|
1048
|
+
if (!current.startsWith(beforeText)) {
|
|
1049
|
+
// 文字は同じだが、大文字と小文字の情報が替わっているなどのパターン
|
|
1050
|
+
// 差し代わりが起きているため、前回値は基準にならないと判断して、差分全体を insertedText とする
|
|
1051
|
+
beforeText = "";
|
|
1052
|
+
insertedText = current;
|
|
1053
|
+
} else {
|
|
1054
|
+
// 末尾に追加されたと考えられる部分を insertedText とする
|
|
1055
|
+
// 例: beforeText="abc" → current="abcde" なら、"de" が insertedText
|
|
1056
|
+
insertedText = current.slice(beforeText.length);
|
|
1057
|
+
}
|
|
1058
|
+
// キャレットは前回値の末尾にあると推測する
|
|
1059
|
+
baseSel = /** @type {SelectionState} */ {
|
|
1060
|
+
start: beforeText.length,
|
|
1061
|
+
end: beforeText.length,
|
|
1062
|
+
direction: "none"
|
|
1063
|
+
};
|
|
1064
|
+
inputType = "insertText";
|
|
1065
|
+
}
|
|
1066
|
+
}
|
|
1067
|
+
}
|
|
1068
|
+
// existBeforeInputEvent は、少なくとも1回 beforeinput が発生したかどうかのフラグ
|
|
1069
|
+
// これが false の場合、上記のような「beforeinputがない環境での推測ロジック」を走らせる。
|
|
1070
|
+
this.existBeforeInputEvent = false;
|
|
1071
|
+
|
|
1029
1072
|
let replaceStart = baseSel.start ?? 0;
|
|
1030
1073
|
let replaceEnd = baseSel.end ?? 0;
|
|
1031
1074
|
|
|
@@ -1038,10 +1081,10 @@ class InputGuard {
|
|
|
1038
1081
|
if (inputType === "deleteContentBackward") {
|
|
1039
1082
|
// Backspace: キャレットの左側1文字を削除
|
|
1040
1083
|
replaceStart = Math.max(0, replaceStart - 1);
|
|
1041
|
-
replaceEnd = snapSel
|
|
1084
|
+
replaceEnd = snapSel?.start ?? replaceEnd;
|
|
1042
1085
|
} else if (inputType === "deleteContentForward") {
|
|
1043
1086
|
// Delete: キャレットの右側1文字を削除
|
|
1044
|
-
replaceStart = snapSel
|
|
1087
|
+
replaceStart = snapSel?.start ?? replaceStart;
|
|
1045
1088
|
replaceEnd = Math.min(beforeText.length, replaceEnd + 1);
|
|
1046
1089
|
}
|
|
1047
1090
|
// 追加で拾うならここ:
|
|
@@ -1248,16 +1291,23 @@ class InputGuard {
|
|
|
1248
1291
|
// console.log("[text-input-guard] input");
|
|
1249
1292
|
// compositionend後に input が来た場合、フォールバックを無効化
|
|
1250
1293
|
this.pendingCompositionCommit = false;
|
|
1251
|
-
|
|
1294
|
+
try {
|
|
1295
|
+
this.evaluateInput();
|
|
1296
|
+
} finally {
|
|
1297
|
+
this.existBeforeInputEvent = false;
|
|
1298
|
+
}
|
|
1252
1299
|
}
|
|
1253
1300
|
|
|
1254
1301
|
/**
|
|
1255
1302
|
* beforeinput:入力が反映される直前に呼ばれる
|
|
1256
1303
|
* - ここでの value/selection が「今回の編集の基準点」になる
|
|
1257
|
-
* @param {
|
|
1304
|
+
* @param {Event} e
|
|
1258
1305
|
* @returns {void}
|
|
1259
1306
|
*/
|
|
1260
1307
|
onBeforeInput(e) {
|
|
1308
|
+
if (!(e instanceof InputEvent)) {
|
|
1309
|
+
return;
|
|
1310
|
+
}
|
|
1261
1311
|
const el = /** @type {HTMLInputElement|HTMLTextAreaElement} */ (this.displayElement);
|
|
1262
1312
|
// 現時点(反映前)の選択範囲
|
|
1263
1313
|
const selection = this.readSelection(el);
|
|
@@ -1265,6 +1315,7 @@ class InputGuard {
|
|
|
1265
1315
|
const inputType = typeof e.inputType === "string" ? e.inputType : null;
|
|
1266
1316
|
/** @type {string|null} */
|
|
1267
1317
|
const insertedText = typeof e.data === "string" ? e.data : null;
|
|
1318
|
+
this.existBeforeInputEvent = true;
|
|
1268
1319
|
this.beforeInputSnapshot = { selection, inputType, insertedText };
|
|
1269
1320
|
}
|
|
1270
1321
|
|
|
@@ -7435,10 +7486,10 @@ const rules = {
|
|
|
7435
7486
|
|
|
7436
7487
|
/**
|
|
7437
7488
|
* バージョン(ビルド時に置換したいならここを差し替える)
|
|
7438
|
-
* 例: rollup replace で ""1.0.
|
|
7489
|
+
* 例: rollup replace で ""1.0.2"" を package.json の version に置換
|
|
7439
7490
|
*/
|
|
7440
7491
|
// @ts-ignore
|
|
7441
7492
|
// eslint-disable-next-line no-undef
|
|
7442
|
-
const version = "1.0.
|
|
7493
|
+
const version = "1.0.2" ;
|
|
7443
7494
|
|
|
7444
7495
|
export { ascii, attach, attachAll, autoAttach, bytes, comma, digits, filter, imeOff, kana, length, numeric, prefix, rules, suffix, trim, version, width };
|