text-input-guard 1.1.0 → 1.2.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.
- package/dist/cjs/text-input-guard.cjs +169 -25
- package/dist/cjs/text-input-guard.min.cjs +1 -1
- package/dist/esm/text-input-guard.js +169 -25
- package/dist/esm/text-input-guard.min.js +1 -1
- package/dist/types/text-input-guard.d.ts +8 -0
- package/dist/umd/text-input-guard.js +169 -25
- package/dist/umd/text-input-guard.min.js +1 -1
- package/package.json +1 -1
|
@@ -412,7 +412,9 @@ class SwapState {
|
|
|
412
412
|
* @property {boolean} [warn] - 非対応ルールなどを console.warn するか
|
|
413
413
|
* @property {string} [invalidClass="is-invalid"] - エラー時に付けるclass名
|
|
414
414
|
* @property {SeparateValueOptions} [separateValue] - 表示値と内部値の分離設定
|
|
415
|
+
* @property {number} [historySize = 50] - 記録する履歴の最大件数
|
|
415
416
|
* @property {(result: ValidateResult) => void} [onValidate] - 評価完了時の通知(input/commitごと)
|
|
417
|
+
* @property {(result: Guard) => void} [onInput] - 入力時に値が変更されていた場合の通知
|
|
416
418
|
* @property {(result: Guard) => void} [onChange] - フォーカスが外れた値が変更されていた場合の通知
|
|
417
419
|
*/
|
|
418
420
|
|
|
@@ -524,6 +526,100 @@ function attachAll(elements, options = {}) {
|
|
|
524
526
|
};
|
|
525
527
|
}
|
|
526
528
|
|
|
529
|
+
/**
|
|
530
|
+
* UndoとRedoを実装用のキュー
|
|
531
|
+
*/
|
|
532
|
+
class HistoryQueue {
|
|
533
|
+
/**
|
|
534
|
+
* 初期化する
|
|
535
|
+
* @param {number} maxLength
|
|
536
|
+
*/
|
|
537
|
+
constructor(maxLength = 50) {
|
|
538
|
+
/**
|
|
539
|
+
* キューの最大長(これ以上は古い履歴から削除される)
|
|
540
|
+
*/
|
|
541
|
+
this.maxLength = maxLength;
|
|
542
|
+
|
|
543
|
+
/**
|
|
544
|
+
* @type {string[]} 履歴キュー(過去から未来の順で値が入る)
|
|
545
|
+
*/
|
|
546
|
+
this.queue = [];
|
|
547
|
+
|
|
548
|
+
/**
|
|
549
|
+
* 現在の位置を示すインデックス(queue内のどこにいるか、-1は履歴なしを意味する)
|
|
550
|
+
*/
|
|
551
|
+
this.index = -1;
|
|
552
|
+
}
|
|
553
|
+
|
|
554
|
+
/**
|
|
555
|
+
* 値を追加(新しい履歴)
|
|
556
|
+
* @param {string} value
|
|
557
|
+
*/
|
|
558
|
+
push(value) {
|
|
559
|
+
// 現在位置の値と同じなら追加しない
|
|
560
|
+
if (this.queue[this.index] === value) {
|
|
561
|
+
return false;
|
|
562
|
+
}
|
|
563
|
+
|
|
564
|
+
// Undo後に新規入力した場合は、現在位置より後ろのRedo履歴を消す
|
|
565
|
+
if (this.index < this.queue.length - 1) {
|
|
566
|
+
this.queue = this.queue.slice(0, this.index + 1);
|
|
567
|
+
}
|
|
568
|
+
|
|
569
|
+
// 念のため、末尾と同じ値も追加しない
|
|
570
|
+
if (this.queue[this.queue.length - 1] === value) {
|
|
571
|
+
this.index = this.queue.length - 1;
|
|
572
|
+
return false;
|
|
573
|
+
}
|
|
574
|
+
|
|
575
|
+
this.queue.push(value);
|
|
576
|
+
|
|
577
|
+
// 最大長制限
|
|
578
|
+
if (this.queue.length > this.maxLength) {
|
|
579
|
+
this.queue.shift();
|
|
580
|
+
}
|
|
581
|
+
|
|
582
|
+
this.index = this.queue.length - 1;
|
|
583
|
+
}
|
|
584
|
+
|
|
585
|
+
/**
|
|
586
|
+
* Undo(前の値)
|
|
587
|
+
* @returns {string|null}
|
|
588
|
+
*/
|
|
589
|
+
undo() {
|
|
590
|
+
if (this.index <= 0) { return null; }
|
|
591
|
+
this.index--;
|
|
592
|
+
return this.queue[this.index];
|
|
593
|
+
}
|
|
594
|
+
|
|
595
|
+
/**
|
|
596
|
+
* Redo(次の値)
|
|
597
|
+
* @returns {string|null}
|
|
598
|
+
*/
|
|
599
|
+
redo() {
|
|
600
|
+
if (this.index >= this.queue.length - 1) { return null; }
|
|
601
|
+
this.index++;
|
|
602
|
+
return this.queue[this.index];
|
|
603
|
+
}
|
|
604
|
+
|
|
605
|
+
/**
|
|
606
|
+
* 初期化して値をセット
|
|
607
|
+
* @param {string} value
|
|
608
|
+
*/
|
|
609
|
+
reset(value) {
|
|
610
|
+
this.queue = [value];
|
|
611
|
+
this.index = 0;
|
|
612
|
+
}
|
|
613
|
+
|
|
614
|
+
/**
|
|
615
|
+
* デバッグ用の文字列化
|
|
616
|
+
* @returns {string}
|
|
617
|
+
*/
|
|
618
|
+
toString() {
|
|
619
|
+
return `HistoryQueue(index=${this.index}, queue=[${this.queue.join(" | ")}])`;
|
|
620
|
+
}
|
|
621
|
+
}
|
|
622
|
+
|
|
527
623
|
class InputGuard {
|
|
528
624
|
/**
|
|
529
625
|
* InputGuard の内部状態を初期化する(DOM/設定/イベント/パイプラインを持つ)
|
|
@@ -577,19 +673,37 @@ class InputGuard {
|
|
|
577
673
|
* attach時に登録されたバリデーション結果コールバック
|
|
578
674
|
* @type {((result: ValidateResult) => void) | undefined}
|
|
579
675
|
*/
|
|
580
|
-
this.
|
|
676
|
+
this.onAttachValidate = options.onValidate;
|
|
677
|
+
|
|
678
|
+
/**
|
|
679
|
+
* input時に値が変更されていた場合の通知
|
|
680
|
+
* @type {((result: Guard) => void) | undefined}
|
|
681
|
+
*/
|
|
682
|
+
this.onAttachInput = options.onInput;
|
|
683
|
+
|
|
684
|
+
/**
|
|
685
|
+
* onInput 判定のための直前の値(input時にこれと比較して変化を検知する)
|
|
686
|
+
* @type {string}
|
|
687
|
+
*/
|
|
688
|
+
this.previousInputValue = "";
|
|
581
689
|
|
|
582
690
|
/**
|
|
583
691
|
* blur時に値が変更されていた場合の通知
|
|
584
692
|
* @type {((result: Guard) => void) | undefined}
|
|
585
693
|
*/
|
|
586
|
-
this.
|
|
694
|
+
this.onAttachChange = options.onChange;
|
|
587
695
|
|
|
588
696
|
/**
|
|
589
697
|
* onChange 判定のための直前の値(blur時にこれと比較して変化を検知する)
|
|
590
698
|
* @type {string}
|
|
591
699
|
*/
|
|
592
|
-
this.
|
|
700
|
+
this.previousBlurValue = "";
|
|
701
|
+
|
|
702
|
+
/**
|
|
703
|
+
* Undo/Redo用の履歴キュー(入力値の履歴を保持して、revert要求に対応するために使用)
|
|
704
|
+
* @type {HistoryQueue}
|
|
705
|
+
*/
|
|
706
|
+
this.history = new HistoryQueue(options.historySize ?? 50);
|
|
593
707
|
|
|
594
708
|
/**
|
|
595
709
|
* 実際に送信を担う要素(swap時は hidden(raw) 側)
|
|
@@ -771,8 +885,10 @@ class InputGuard {
|
|
|
771
885
|
this.bindEvents();
|
|
772
886
|
// 初期値を評価
|
|
773
887
|
this.evaluateCommit();
|
|
774
|
-
//
|
|
775
|
-
this.
|
|
888
|
+
// 初期値を記録(onInput/onChangeの比較用)
|
|
889
|
+
this.previousInputValue = this.getRawValue();
|
|
890
|
+
this.previousBlurValue = this.getDisplayValue();
|
|
891
|
+
this.history.push(this.getRawValue());
|
|
776
892
|
}
|
|
777
893
|
|
|
778
894
|
/**
|
|
@@ -1047,7 +1163,7 @@ class InputGuard {
|
|
|
1047
1163
|
*/
|
|
1048
1164
|
createCtx({ useSnapshot = true } = {}) {
|
|
1049
1165
|
// 入力後のテキストを取得
|
|
1050
|
-
|
|
1166
|
+
let afterText = /** @type {HTMLInputElement|HTMLTextAreaElement} */ (this.displayElement).value;
|
|
1051
1167
|
const snap = useSnapshot ? this.beforeInputSnapshot : null;
|
|
1052
1168
|
let inputType = snap?.inputType ?? "";
|
|
1053
1169
|
let insertedText = snap?.insertedText ?? "";
|
|
@@ -1146,10 +1262,21 @@ class InputGuard {
|
|
|
1146
1262
|
|
|
1147
1263
|
// アンドゥリドゥの特殊処理
|
|
1148
1264
|
if (inputType === "historyUndo" || inputType === "historyRedo") {
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
|
|
1265
|
+
let newText = null;
|
|
1266
|
+
// console.log(inputType);
|
|
1267
|
+
// console.log(this.history.toString());
|
|
1268
|
+
if (inputType === "historyUndo") {
|
|
1269
|
+
newText = this.history.undo();
|
|
1270
|
+
} else if (inputType === "historyRedo") {
|
|
1271
|
+
newText = this.history.redo();
|
|
1272
|
+
}
|
|
1273
|
+
if (newText !== null) {
|
|
1274
|
+
afterText = newText;
|
|
1275
|
+
const diff = this.detectTextDiff(beforeText, afterText);
|
|
1276
|
+
replaceStart = diff.replaceStart;
|
|
1277
|
+
replaceEnd = diff.replaceEnd;
|
|
1278
|
+
insertedText = diff.insertedText;
|
|
1279
|
+
}
|
|
1153
1280
|
}
|
|
1154
1281
|
|
|
1155
1282
|
return {
|
|
@@ -1195,13 +1322,13 @@ class InputGuard {
|
|
|
1195
1322
|
* @returns {void}
|
|
1196
1323
|
*/
|
|
1197
1324
|
notifyValidate(source) {
|
|
1198
|
-
if (!this.
|
|
1325
|
+
if (!this.onAttachValidate) {
|
|
1199
1326
|
return;
|
|
1200
1327
|
}
|
|
1201
1328
|
|
|
1202
1329
|
const errors = this.getErrors();
|
|
1203
1330
|
|
|
1204
|
-
this.
|
|
1331
|
+
this.onAttachValidate({
|
|
1205
1332
|
guard: this.getGuard(),
|
|
1206
1333
|
source,
|
|
1207
1334
|
errors,
|
|
@@ -1390,10 +1517,10 @@ class InputGuard {
|
|
|
1390
1517
|
onBlur() {
|
|
1391
1518
|
// console.log("[text-input-guard] blur");
|
|
1392
1519
|
this.evaluateCommit();
|
|
1393
|
-
if (this.
|
|
1394
|
-
this.
|
|
1395
|
-
if (this.
|
|
1396
|
-
this.
|
|
1520
|
+
if (this.previousBlurValue !== this.getDisplayValue()) {
|
|
1521
|
+
this.previousBlurValue = this.getDisplayValue();
|
|
1522
|
+
if (this.onAttachChange) {
|
|
1523
|
+
this.onAttachChange(this.getGuard());
|
|
1397
1524
|
}
|
|
1398
1525
|
}
|
|
1399
1526
|
}
|
|
@@ -1414,21 +1541,27 @@ class InputGuard {
|
|
|
1414
1541
|
ctx.beforeText = "";
|
|
1415
1542
|
ctx.afterText = current;
|
|
1416
1543
|
|
|
1417
|
-
let
|
|
1418
|
-
|
|
1419
|
-
|
|
1544
|
+
let raw = current;
|
|
1545
|
+
raw = this.runNormalizeChar(raw, ctx);
|
|
1546
|
+
raw = this.runNormalizeStructure(raw, ctx);
|
|
1420
1547
|
|
|
1421
|
-
if (
|
|
1422
|
-
this.setDisplayValuePreserveCaret(display,
|
|
1423
|
-
this.syncRaw(
|
|
1548
|
+
if (raw !== current) {
|
|
1549
|
+
this.setDisplayValuePreserveCaret(display, raw, ctx);
|
|
1550
|
+
this.syncRaw(raw);
|
|
1424
1551
|
}
|
|
1425
1552
|
|
|
1426
1553
|
// 受理値更新(blockで戻す位置も自然になる)
|
|
1427
|
-
this.lastAcceptedValue =
|
|
1554
|
+
this.lastAcceptedValue = raw;
|
|
1428
1555
|
this.lastAcceptedSelection = this.readSelection(display);
|
|
1429
1556
|
|
|
1430
1557
|
// キャレット/選択範囲の変化も反映しておく(blockで戻す位置も自然になる)
|
|
1431
1558
|
this.onSelectionChange();
|
|
1559
|
+
|
|
1560
|
+
// previousInputValueも更新(次の入力で差分再構成の基準になる)
|
|
1561
|
+
this.previousInputValue = raw;
|
|
1562
|
+
|
|
1563
|
+
// 中の値が替わっている可能性を考えて、historyも更新しておく(undoしたときに不自然にならないように)
|
|
1564
|
+
this.history.push(raw);
|
|
1432
1565
|
}
|
|
1433
1566
|
|
|
1434
1567
|
/**
|
|
@@ -1624,6 +1757,17 @@ class InputGuard {
|
|
|
1624
1757
|
|
|
1625
1758
|
// コールバック関数処理
|
|
1626
1759
|
this.notifyValidate("input");
|
|
1760
|
+
if (this.previousInputValue !== raw) {
|
|
1761
|
+
this.previousInputValue = raw;
|
|
1762
|
+
|
|
1763
|
+
// historyに積む(undoの基準になる)
|
|
1764
|
+
this.history.push(raw);
|
|
1765
|
+
|
|
1766
|
+
// 変更コールバック
|
|
1767
|
+
if (this.onAttachInput) {
|
|
1768
|
+
this.onAttachInput(this.getGuard());
|
|
1769
|
+
}
|
|
1770
|
+
}
|
|
1627
1771
|
}
|
|
1628
1772
|
|
|
1629
1773
|
/**
|
|
@@ -7557,11 +7701,11 @@ const rules = {
|
|
|
7557
7701
|
|
|
7558
7702
|
/**
|
|
7559
7703
|
* バージョン(ビルド時に置換したいならここを差し替える)
|
|
7560
|
-
* 例: rollup replace で ""1.1
|
|
7704
|
+
* 例: rollup replace で ""1.2.1"" を package.json の version に置換
|
|
7561
7705
|
*/
|
|
7562
7706
|
// @ts-ignore
|
|
7563
7707
|
// eslint-disable-next-line no-undef
|
|
7564
|
-
const version = "1.1
|
|
7708
|
+
const version = "1.2.1" ;
|
|
7565
7709
|
|
|
7566
7710
|
/**
|
|
7567
7711
|
* UMD公開時のグローバルオブジェクト
|