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