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.
@@ -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
 
@@ -752,7 +758,7 @@
752
758
  this.applySeparateValue();
753
759
  this.bindEvents();
754
760
  // 初期値を評価
755
- this.evaluateInput();
761
+ this.evaluateCommit();
756
762
  }
757
763
 
758
764
  /**
@@ -1044,10 +1050,10 @@
1044
1050
  if (inputType === "deleteContentBackward") {
1045
1051
  // Backspace: キャレットの左側1文字を削除
1046
1052
  replaceStart = Math.max(0, replaceStart - 1);
1047
- replaceEnd = snapSel.start ?? replaceEnd;
1053
+ replaceEnd = snapSel?.start ?? replaceEnd;
1048
1054
  } else if (inputType === "deleteContentForward") {
1049
1055
  // Delete: キャレットの右側1文字を削除
1050
- replaceStart = snapSel.start ?? replaceStart;
1056
+ replaceStart = snapSel?.start ?? replaceStart;
1051
1057
  replaceEnd = Math.min(beforeText.length, replaceEnd + 1);
1052
1058
  }
1053
1059
  // 追加で拾うならここ:
@@ -1254,16 +1260,25 @@
1254
1260
  // console.log("[text-input-guard] input");
1255
1261
  // compositionend後に input が来た場合、フォールバックを無効化
1256
1262
  this.pendingCompositionCommit = false;
1257
- this.evaluateInput();
1263
+ try {
1264
+ this.evaluateInput();
1265
+ } finally {
1266
+ // beforeinput が来ない入力経路(autocomplete等)で
1267
+ // 古い snapshot を使い回さないよう、1イベントごとに破棄する
1268
+ this.beforeInputSnapshot = null;
1269
+ }
1258
1270
  }
1259
1271
 
1260
1272
  /**
1261
1273
  * beforeinput:入力が反映される直前に呼ばれる
1262
1274
  * - ここでの value/selection が「今回の編集の基準点」になる
1263
- * @param {InputEvent} e
1275
+ * @param {Event} e
1264
1276
  * @returns {void}
1265
1277
  */
1266
1278
  onBeforeInput(e) {
1279
+ if (!(e instanceof InputEvent)) {
1280
+ return;
1281
+ }
1267
1282
  const el = /** @type {HTMLInputElement|HTMLTextAreaElement} */ (this.displayElement);
1268
1283
  // 現時点(反映前)の選択範囲
1269
1284
  const selection = this.readSelection(el);
@@ -1410,6 +1425,45 @@
1410
1425
  const ctx = this.createCtx();
1411
1426
  ctx.afterText = current;
1412
1427
 
1428
+ /**
1429
+ * 入力値情報のみを使用するフォールバック
1430
+ * @returns {GuardContext}
1431
+ */
1432
+ const applyFullNormalizeFromCurrent = () => {
1433
+ let newText = current;
1434
+ ctx.beforeText = "";
1435
+ newText = this.runNormalizeChar(newText, ctx);
1436
+ newText = this.runNormalizeStructure(newText, ctx);
1437
+ this.setDisplayValuePreserveCaret(display, newText, ctx);
1438
+ ctx.afterText = newText;
1439
+ return ctx;
1440
+ };
1441
+
1442
+ // beforeinput が取得できない経路(初回評価)では
1443
+ // 差分再構成を行うと lastAcceptedValue 基準で値を落とす可能性があるため、
1444
+ // 現在の全文を正規化して扱うフォールバックへ切り替える。
1445
+ if (!this.beforeInputSnapshot) {
1446
+ return applyFullNormalizeFromCurrent();
1447
+ }
1448
+
1449
+ // オートコンプリート等では beforeinput は来ても data が空のことがあり、
1450
+ // 差分情報だけでは再構成不能になる。表示値がすでに変わっている場合は
1451
+ // 再構成を諦めて current 全体の正規化に切り替える。
1452
+ const isDeleteInput =
1453
+ ctx.inputType === "deleteContentBackward" ||
1454
+ ctx.inputType === "deleteContentForward";
1455
+ const isInsertLikeInput =
1456
+ ctx.inputType === "" ||
1457
+ ctx.inputType?.startsWith("insert");
1458
+ const lacksDelta =
1459
+ ctx.insertedText === "" &&
1460
+ ctx.beforeText !== current &&
1461
+ isInsertLikeInput &&
1462
+ !isDeleteInput;
1463
+ if (lacksDelta) {
1464
+ return applyFullNormalizeFromCurrent();
1465
+ }
1466
+
1413
1467
  // 元のテキスト
1414
1468
  const beforeText = ctx.beforeText;
1415
1469
 
@@ -7441,11 +7495,11 @@
7441
7495
 
7442
7496
  /**
7443
7497
  * バージョン(ビルド時に置換したいならここを差し替える)
7444
- * 例: rollup replace で ""1.0.0"" を package.json の version に置換
7498
+ * 例: rollup replace で ""1.0.1"" を package.json の version に置換
7445
7499
  */
7446
7500
  // @ts-ignore
7447
7501
  // eslint-disable-next-line no-undef
7448
- const version = "1.0.0" ;
7502
+ const version = "1.0.1" ;
7449
7503
 
7450
7504
  exports.ascii = ascii;
7451
7505
  exports.attach = attach;