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