Skip to content

Commit e87baad

Browse files
committed
文字の制御が入らない場合はアンドゥ/リドゥを行える機能を追加
1 parent 6a526f8 commit e87baad

3 files changed

Lines changed: 50 additions & 3 deletions

File tree

HISTORY.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
- `attach` の引数に `onChange` を追加
66
- `textarea` に対して `attach` を実施した場合に改行できなかった不具合を修正
7+
- 文字の制御が入らない場合はアンドゥ/リドゥを行える機能を追加
78

89
#### 1.0.2 2026/4/24
910

examples/dev/textarea.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ const guard = attach(input, {
1111
target: "katakana-full"
1212
}),
1313
rules.filter({
14-
category: ["katakana-full"],
14+
category: ["katakana-full", "ascii"],
1515
allow: /[ \r\n]/
1616
}),
1717
rules.length({

src/text-input-guard.js

Lines changed: 48 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -737,6 +737,42 @@ class InputGuard {
737737
}
738738
}
739739

740+
/**
741+
* 変更前後の文字列から置換範囲と挿入文字列を推測
742+
* @param {string} beforeText
743+
* @param {string} afterText
744+
* @returns {{ replaceStart: number, replaceEnd: number, insertedText: string }}
745+
*/
746+
detectTextDiff(beforeText, afterText) {
747+
let start = 0;
748+
749+
while (
750+
start < beforeText.length &&
751+
start < afterText.length &&
752+
beforeText[start] === afterText[start]
753+
) {
754+
start++;
755+
}
756+
757+
let beforeEnd = beforeText.length;
758+
let afterEnd = afterText.length;
759+
760+
while (
761+
beforeEnd > start &&
762+
afterEnd > start &&
763+
beforeText[beforeEnd - 1] === afterText[afterEnd - 1]
764+
) {
765+
beforeEnd--;
766+
afterEnd--;
767+
}
768+
769+
return {
770+
replaceStart: start,
771+
replaceEnd: beforeEnd,
772+
insertedText: afterText.slice(start, afterEnd)
773+
};
774+
}
775+
740776
/**
741777
* ルール実行に渡すコンテキストを作る(pushErrorで errors に積める)
742778
* @returns {GuardContext}
@@ -786,7 +822,9 @@ class InputGuard {
786822
baseSel = lastSel;
787823
}
788824

789-
// beforeinput がない環境では、差分再構成の基準が「前回の受理値」しかないため、そこから今回の編集内容を推測する必要がある。
825+
// オートコンプリートの処理
826+
// inputType が取得できないため existBeforeInputEvent 情報で判断
827+
// 差分再構成の基準が「前回の受理値」しかないため、そこから今回の編集内容を推測する必要がある。
790828
if (beforeText.length === 0 || !this.existBeforeInputEvent) {
791829
// 前回の値がとれないものの、何かしら入力情報がある状態
792830
if (afterText.length > 0) {
@@ -819,10 +857,10 @@ class InputGuard {
819857
let replaceStart = baseSel.start ?? 0;
820858
let replaceEnd = baseSel.end ?? 0;
821859

860+
// 削除操作の特殊処理
822861
// Backspace / Delete は「挿入文字がない(dataがnull)」ことが多い。
823862
// そのままだと差分再構成で “何も変わらない” 扱いになって削除が効かなくなるため、
824863
// 選択範囲が無い場合は「削除される1文字ぶん」の置換範囲をここで作る。
825-
//
826864
// ※ 選択範囲がある削除は replaceStart!=replaceEnd なので補正不要(その範囲を消すだけでよい)
827865
if (replaceStart === replaceEnd) {
828866
if (inputType === "deleteContentBackward") {
@@ -838,6 +876,14 @@ class InputGuard {
838876
// deleteWordBackward / deleteWordForward / deleteByCut / deleteSoftLineBackward ... etc
839877
}
840878

879+
// アンドゥリドゥの特殊処理
880+
if (inputType === "historyUndo" || inputType === "historyRedo") {
881+
const diff = this.detectTextDiff(beforeText, afterText);
882+
replaceStart = diff.replaceStart;
883+
replaceEnd = diff.replaceEnd;
884+
insertedText = diff.insertedText;
885+
}
886+
841887
return {
842888
hostElement: this.hostElement,
843889
displayElement: this.displayElement,

0 commit comments

Comments
 (0)