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