text-input-guard 1.0.0 → 1.0.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.
@@ -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
 
@@ -746,7 +752,7 @@ class InputGuard {
746
752
  this.applySeparateValue();
747
753
  this.bindEvents();
748
754
  // 初期値を評価
749
- this.evaluateInput();
755
+ this.evaluateCommit();
750
756
  }
751
757
 
752
758
  /**
@@ -1038,10 +1044,10 @@ class InputGuard {
1038
1044
  if (inputType === "deleteContentBackward") {
1039
1045
  // Backspace: キャレットの左側1文字を削除
1040
1046
  replaceStart = Math.max(0, replaceStart - 1);
1041
- replaceEnd = snapSel.start ?? replaceEnd;
1047
+ replaceEnd = snapSel?.start ?? replaceEnd;
1042
1048
  } else if (inputType === "deleteContentForward") {
1043
1049
  // Delete: キャレットの右側1文字を削除
1044
- replaceStart = snapSel.start ?? replaceStart;
1050
+ replaceStart = snapSel?.start ?? replaceStart;
1045
1051
  replaceEnd = Math.min(beforeText.length, replaceEnd + 1);
1046
1052
  }
1047
1053
  // 追加で拾うならここ:
@@ -1248,16 +1254,25 @@ class InputGuard {
1248
1254
  // console.log("[text-input-guard] input");
1249
1255
  // compositionend後に input が来た場合、フォールバックを無効化
1250
1256
  this.pendingCompositionCommit = false;
1251
- this.evaluateInput();
1257
+ try {
1258
+ this.evaluateInput();
1259
+ } finally {
1260
+ // beforeinput が来ない入力経路(autocomplete等)で
1261
+ // 古い snapshot を使い回さないよう、1イベントごとに破棄する
1262
+ this.beforeInputSnapshot = null;
1263
+ }
1252
1264
  }
1253
1265
 
1254
1266
  /**
1255
1267
  * beforeinput:入力が反映される直前に呼ばれる
1256
1268
  * - ここでの value/selection が「今回の編集の基準点」になる
1257
- * @param {InputEvent} e
1269
+ * @param {Event} e
1258
1270
  * @returns {void}
1259
1271
  */
1260
1272
  onBeforeInput(e) {
1273
+ if (!(e instanceof InputEvent)) {
1274
+ return;
1275
+ }
1261
1276
  const el = /** @type {HTMLInputElement|HTMLTextAreaElement} */ (this.displayElement);
1262
1277
  // 現時点(反映前)の選択範囲
1263
1278
  const selection = this.readSelection(el);
@@ -1404,6 +1419,45 @@ class InputGuard {
1404
1419
  const ctx = this.createCtx();
1405
1420
  ctx.afterText = current;
1406
1421
 
1422
+ /**
1423
+ * 入力値情報のみを使用するフォールバック
1424
+ * @returns {GuardContext}
1425
+ */
1426
+ const applyFullNormalizeFromCurrent = () => {
1427
+ let newText = current;
1428
+ ctx.beforeText = "";
1429
+ newText = this.runNormalizeChar(newText, ctx);
1430
+ newText = this.runNormalizeStructure(newText, ctx);
1431
+ this.setDisplayValuePreserveCaret(display, newText, ctx);
1432
+ ctx.afterText = newText;
1433
+ return ctx;
1434
+ };
1435
+
1436
+ // beforeinput が取得できない経路(初回評価)では
1437
+ // 差分再構成を行うと lastAcceptedValue 基準で値を落とす可能性があるため、
1438
+ // 現在の全文を正規化して扱うフォールバックへ切り替える。
1439
+ if (!this.beforeInputSnapshot) {
1440
+ return applyFullNormalizeFromCurrent();
1441
+ }
1442
+
1443
+ // オートコンプリート等では beforeinput は来ても data が空のことがあり、
1444
+ // 差分情報だけでは再構成不能になる。表示値がすでに変わっている場合は
1445
+ // 再構成を諦めて current 全体の正規化に切り替える。
1446
+ const isDeleteInput =
1447
+ ctx.inputType === "deleteContentBackward" ||
1448
+ ctx.inputType === "deleteContentForward";
1449
+ const isInsertLikeInput =
1450
+ ctx.inputType === "" ||
1451
+ ctx.inputType?.startsWith("insert");
1452
+ const lacksDelta =
1453
+ ctx.insertedText === "" &&
1454
+ ctx.beforeText !== current &&
1455
+ isInsertLikeInput &&
1456
+ !isDeleteInput;
1457
+ if (lacksDelta) {
1458
+ return applyFullNormalizeFromCurrent();
1459
+ }
1460
+
1407
1461
  // 元のテキスト
1408
1462
  const beforeText = ctx.beforeText;
1409
1463
 
@@ -7435,10 +7489,10 @@ const rules = {
7435
7489
 
7436
7490
  /**
7437
7491
  * バージョン(ビルド時に置換したいならここを差し替える)
7438
- * 例: rollup replace で ""1.0.0"" を package.json の version に置換
7492
+ * 例: rollup replace で ""1.0.1"" を package.json の version に置換
7439
7493
  */
7440
7494
  // @ts-ignore
7441
7495
  // eslint-disable-next-line no-undef
7442
- const version = "1.0.0" ;
7496
+ const version = "1.0.1" ;
7443
7497
 
7444
7498
  export { ascii, attach, attachAll, autoAttach, bytes, comma, digits, filter, imeOff, kana, length, numeric, prefix, rules, suffix, trim, version, width };