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