text-input-guard 0.2.1 → 0.2.2
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/README.md +14 -6
- package/dist/cjs/text-input-guard.cjs +87 -27
- package/dist/cjs/text-input-guard.min.cjs +1 -1
- package/dist/esm/text-input-guard.js +87 -27
- package/dist/esm/text-input-guard.min.js +1 -1
- package/dist/umd/text-input-guard.js +87 -27
- package/dist/umd/text-input-guard.min.js +1 -1
- package/package.json +1 -1
|
@@ -163,6 +163,7 @@ class SwapState {
|
|
|
163
163
|
// raw化(送信担当)
|
|
164
164
|
input.type = "hidden";
|
|
165
165
|
input.removeAttribute("id");
|
|
166
|
+
input.removeAttribute("class");
|
|
166
167
|
input.className = "";
|
|
167
168
|
input.dataset.tigRole = "raw";
|
|
168
169
|
|
|
@@ -170,6 +171,9 @@ class SwapState {
|
|
|
170
171
|
if (this.originalId) {
|
|
171
172
|
input.dataset.tigOriginalId = this.originalId;
|
|
172
173
|
}
|
|
174
|
+
if (this.originalClass) {
|
|
175
|
+
input.dataset.tigOriginalClass = this.originalClass;
|
|
176
|
+
}
|
|
173
177
|
if (this.originalName) {
|
|
174
178
|
input.dataset.tigOriginalName = this.originalName;
|
|
175
179
|
}
|
|
@@ -190,7 +194,10 @@ class SwapState {
|
|
|
190
194
|
display.id = this.originalId;
|
|
191
195
|
}
|
|
192
196
|
|
|
193
|
-
|
|
197
|
+
if (this.originalClass) {
|
|
198
|
+
display.className = this.originalClass;
|
|
199
|
+
}
|
|
200
|
+
|
|
194
201
|
display.value = raw.value;
|
|
195
202
|
|
|
196
203
|
for (const [name, v] of Object.entries(this.originalUiAttrs)) {
|
|
@@ -251,6 +258,7 @@ class SwapState {
|
|
|
251
258
|
|
|
252
259
|
delete raw.dataset.tigRole;
|
|
253
260
|
delete raw.dataset.tigOriginalId;
|
|
261
|
+
delete raw.dataset.tigOriginalClass;
|
|
254
262
|
delete raw.dataset.tigOriginalName;
|
|
255
263
|
}
|
|
256
264
|
}
|
|
@@ -444,6 +452,28 @@ function warnLog(msg, warn) {
|
|
|
444
452
|
}
|
|
445
453
|
}
|
|
446
454
|
|
|
455
|
+
/**
|
|
456
|
+
* input / textarea 要素と内部 Guard インスタンスの対応表
|
|
457
|
+
*
|
|
458
|
+
* - key: displayElement
|
|
459
|
+
* - value: InputGuard(内部実装)
|
|
460
|
+
*
|
|
461
|
+
* @type {WeakMap<HTMLInputElement|HTMLTextAreaElement, InputGuard>}
|
|
462
|
+
*/
|
|
463
|
+
const guardMap = new WeakMap();
|
|
464
|
+
|
|
465
|
+
document.addEventListener("selectionchange", () => {
|
|
466
|
+
const el = document.activeElement;
|
|
467
|
+
if (!(el instanceof HTMLInputElement || el instanceof HTMLTextAreaElement)) {
|
|
468
|
+
return;
|
|
469
|
+
}
|
|
470
|
+
const inputGuard = guardMap.get(el);
|
|
471
|
+
if (!inputGuard) {
|
|
472
|
+
return;
|
|
473
|
+
}
|
|
474
|
+
inputGuard.onSelectionChange();
|
|
475
|
+
});
|
|
476
|
+
|
|
447
477
|
/**
|
|
448
478
|
* 指定した1要素に対してガードを適用し、Guard API を返す
|
|
449
479
|
* @param {HTMLInputElement|HTMLTextAreaElement} element
|
|
@@ -451,9 +481,12 @@ function warnLog(msg, warn) {
|
|
|
451
481
|
* @returns {Guard}
|
|
452
482
|
*/
|
|
453
483
|
function attach(element, options = {}) {
|
|
454
|
-
const
|
|
455
|
-
|
|
456
|
-
|
|
484
|
+
const inputGuard = new InputGuard(element, options);
|
|
485
|
+
inputGuard.init();
|
|
486
|
+
const guard = inputGuard.getGuard();
|
|
487
|
+
const display = guard.getDisplayElement();
|
|
488
|
+
guardMap.set(display, inputGuard);
|
|
489
|
+
return guard;
|
|
457
490
|
}
|
|
458
491
|
|
|
459
492
|
/**
|
|
@@ -549,7 +582,7 @@ class InputGuard {
|
|
|
549
582
|
/**
|
|
550
583
|
* ユーザーが直接入力する表示側要素
|
|
551
584
|
* swapしない場合は originalElement と同一
|
|
552
|
-
* @type {
|
|
585
|
+
* @type {HTMLInputElement|HTMLTextAreaElement}
|
|
553
586
|
*/
|
|
554
587
|
this.displayElement = element;
|
|
555
588
|
|
|
@@ -671,6 +704,12 @@ class InputGuard {
|
|
|
671
704
|
*/
|
|
672
705
|
this.pendingCompositionCommit = false;
|
|
673
706
|
|
|
707
|
+
/**
|
|
708
|
+
* selection 更新のフレーム予約ID
|
|
709
|
+
* @type {number|null}
|
|
710
|
+
*/
|
|
711
|
+
this.selectionFrameId = null;
|
|
712
|
+
|
|
674
713
|
/**
|
|
675
714
|
* 直前に受理した表示値、正しい情報のスナップショットのような情報(block時の戻し先)
|
|
676
715
|
* @type {string}
|
|
@@ -716,9 +755,11 @@ class InputGuard {
|
|
|
716
755
|
* @returns {SelectionState}
|
|
717
756
|
*/
|
|
718
757
|
readSelection(el) {
|
|
758
|
+
const start = el.selectionStart ?? 0;
|
|
759
|
+
const end = el.selectionEnd ?? start;
|
|
719
760
|
return {
|
|
720
|
-
start
|
|
721
|
-
end
|
|
761
|
+
start,
|
|
762
|
+
end,
|
|
722
763
|
direction: el.selectionDirection
|
|
723
764
|
};
|
|
724
765
|
}
|
|
@@ -832,6 +873,8 @@ class InputGuard {
|
|
|
832
873
|
* @returns {void}
|
|
833
874
|
*/
|
|
834
875
|
detach() {
|
|
876
|
+
// 管理マップから削除
|
|
877
|
+
guardMap.delete(this.displayElement);
|
|
835
878
|
// イベント解除(displayElementがswap後の可能性があるので先に外す)
|
|
836
879
|
this.unbindEvents();
|
|
837
880
|
// swap復元
|
|
@@ -892,15 +935,7 @@ class InputGuard {
|
|
|
892
935
|
this.displayElement.addEventListener("input", this.onInput);
|
|
893
936
|
this.displayElement.addEventListener("beforeinput", this.onBeforeInput);
|
|
894
937
|
this.displayElement.addEventListener("blur", this.onBlur);
|
|
895
|
-
|
|
896
|
-
// フォーカスで編集用に戻す
|
|
897
938
|
this.displayElement.addEventListener("focus", this.onFocus);
|
|
898
|
-
|
|
899
|
-
// キャレット/選択範囲の変化を拾う(block時の不自然ジャンプ対策)
|
|
900
|
-
this.displayElement.addEventListener("keyup", this.onSelectionChange);
|
|
901
|
-
this.displayElement.addEventListener("mouseup", this.onSelectionChange);
|
|
902
|
-
this.displayElement.addEventListener("select", this.onSelectionChange);
|
|
903
|
-
this.displayElement.addEventListener("focus", this.onSelectionChange);
|
|
904
939
|
}
|
|
905
940
|
|
|
906
941
|
/**
|
|
@@ -914,10 +949,6 @@ class InputGuard {
|
|
|
914
949
|
this.displayElement.removeEventListener("beforeinput", this.onBeforeInput);
|
|
915
950
|
this.displayElement.removeEventListener("blur", this.onBlur);
|
|
916
951
|
this.displayElement.removeEventListener("focus", this.onFocus);
|
|
917
|
-
this.displayElement.removeEventListener("keyup", this.onSelectionChange);
|
|
918
|
-
this.displayElement.removeEventListener("mouseup", this.onSelectionChange);
|
|
919
|
-
this.displayElement.removeEventListener("select", this.onSelectionChange);
|
|
920
|
-
this.displayElement.removeEventListener("focus", this.onSelectionChange);
|
|
921
952
|
}
|
|
922
953
|
|
|
923
954
|
/**
|
|
@@ -1285,12 +1316,41 @@ class InputGuard {
|
|
|
1285
1316
|
* @returns {void}
|
|
1286
1317
|
*/
|
|
1287
1318
|
onSelectionChange() {
|
|
1288
|
-
|
|
1289
|
-
|
|
1290
|
-
|
|
1319
|
+
const requestFrame =
|
|
1320
|
+
typeof requestAnimationFrame === "function"
|
|
1321
|
+
? requestAnimationFrame
|
|
1322
|
+
: (
|
|
1323
|
+
/** @param {FrameRequestCallback} cb */
|
|
1324
|
+
(cb) => setTimeout(cb, 0)
|
|
1325
|
+
);
|
|
1326
|
+
|
|
1327
|
+
const cancelFrame =
|
|
1328
|
+
typeof cancelAnimationFrame === "function"
|
|
1329
|
+
? cancelAnimationFrame
|
|
1330
|
+
: clearTimeout;
|
|
1331
|
+
|
|
1332
|
+
// すでに予約済みならキャンセル(selectionchange は連続発火するため)
|
|
1333
|
+
if (this.selectionFrameId != null) {
|
|
1334
|
+
cancelFrame(this.selectionFrameId);
|
|
1291
1335
|
}
|
|
1292
|
-
|
|
1293
|
-
this.
|
|
1336
|
+
|
|
1337
|
+
this.selectionFrameId = requestFrame(() => {
|
|
1338
|
+
this.selectionFrameId = null;
|
|
1339
|
+
|
|
1340
|
+
// IME変換中は無視(キャレット位置が不安定になるため)
|
|
1341
|
+
if (this.composing) {
|
|
1342
|
+
return;
|
|
1343
|
+
}
|
|
1344
|
+
|
|
1345
|
+
const el = /** @type {HTMLInputElement|HTMLTextAreaElement} */ (this.displayElement);
|
|
1346
|
+
|
|
1347
|
+
// 要素がフォーカスされていない場合は無視
|
|
1348
|
+
if (document.activeElement !== el) {
|
|
1349
|
+
return;
|
|
1350
|
+
}
|
|
1351
|
+
|
|
1352
|
+
this.lastAcceptedSelection = this.readSelection(el);
|
|
1353
|
+
});
|
|
1294
1354
|
}
|
|
1295
1355
|
|
|
1296
1356
|
/**
|
|
@@ -7029,7 +7089,7 @@ function bytes(options = {}) {
|
|
|
7029
7089
|
code: "bytes.max_overflow",
|
|
7030
7090
|
rule: "bytes",
|
|
7031
7091
|
phase: "validate",
|
|
7032
|
-
detail: {
|
|
7092
|
+
detail: { limit: opt.max, actual: len }
|
|
7033
7093
|
});
|
|
7034
7094
|
}
|
|
7035
7095
|
}
|
|
@@ -7375,10 +7435,10 @@ const rules = {
|
|
|
7375
7435
|
|
|
7376
7436
|
/**
|
|
7377
7437
|
* バージョン(ビルド時に置換したいならここを差し替える)
|
|
7378
|
-
* 例: rollup replace で ""0.2.
|
|
7438
|
+
* 例: rollup replace で ""0.2.2"" を package.json の version に置換
|
|
7379
7439
|
*/
|
|
7380
7440
|
// @ts-ignore
|
|
7381
7441
|
// eslint-disable-next-line no-undef
|
|
7382
|
-
const version = "0.2.
|
|
7442
|
+
const version = "0.2.2" ;
|
|
7383
7443
|
|
|
7384
7444
|
export { ascii, attach, attachAll, autoAttach, bytes, comma, digits, filter, imeOff, kana, length, numeric, prefix, rules, suffix, trim, version, width };
|