@aiquants/virtualscroll 2.4.0 → 2.6.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.
@@ -0,0 +1,90 @@
1
+ /**
2
+ * @module wheelAxes
3
+ * @description Single source of truth for how a wheel event is split into scroll axes.
4
+ * Used only by `ScrollPane.applyWheel`, which is itself the single seam every wheel caller goes
5
+ * through, so the rules cannot drift between the pane and elements outside it.
6
+ *
7
+ * @description ホイールイベントをスクロール軸へ分解する規則の単一の真実。呼び出し元は
8
+ * `ScrollPane.applyWheel` **だけ**であり、その `applyWheel` がすべてのホイール呼び出し元
9
+ * (ペイン根のリスナー / `useWheelBridge` / 消費側の直接呼び出し) の唯一のシームなので、
10
+ * ペイン内外で規則が食い違わない。
11
+ *
12
+ * ❗ **公開 API ではない** (`index.ts` から export しない)。公開すると `LINE_HEIGHT_PX = 16`・
13
+ * 同値時の横倒し・4 フィールドの形が恒久的に凍結される。
14
+ */
15
+
16
+ /** Line height (px) assumed for `DOM_DELTA_LINE`. / `DOM_DELTA_LINE` で仮定する 1 行の高さ (px)。 */
17
+ const LINE_HEIGHT_PX = 16
18
+
19
+ /**
20
+ * Wheel axes resolved from a raw wheel event.
21
+ * 生のホイールイベントから解決した軸情報。
22
+ */
23
+ export type ResolvedWheelAxes = {
24
+ /** True when ctrl is held, i.e. the browser treats it as zoom. Deltas are still reported as-is. / ctrl 押下 (ブラウザがズームとして扱う) の場合に true。デルタはそのまま報告する。 */
25
+ isZoomGesture: boolean
26
+ /** True when the horizontal component dominates (includes shift+wheel). / 横成分が優勢な場合に true (shift+ホイールを含む)。 */
27
+ isHorizontalDominant: boolean
28
+ /** Horizontal delta in px (deltaMode already converted). / px 単位の横方向デルタ (deltaMode 変換済み)。 */
29
+ horizontalDelta: number
30
+ /** Vertical delta in px (deltaMode already converted). / px 単位の縦方向デルタ (deltaMode 変換済み)。 */
31
+ verticalDelta: number
32
+ }
33
+
34
+ /**
35
+ * Converts a raw delta to pixels according to the event's `deltaMode`.
36
+ * イベントの `deltaMode` に従って生のデルタを px へ変換する処理。
37
+ *
38
+ * @param delta - Raw delta from the event / イベントの生デルタ
39
+ * @param deltaMode - `WheelEvent.deltaMode` / `WheelEvent.deltaMode`
40
+ * @param viewportSize - Viewport length used for `DOM_DELTA_PAGE` / `DOM_DELTA_PAGE` で使うビューポート長
41
+ * @returns The delta in pixels / px 単位のデルタ
42
+ */
43
+ const toPixelDelta = (delta: number, deltaMode: number, viewportSize: number): number => {
44
+ if (deltaMode === 1) {
45
+ // DOM_DELTA_LINE: 行単位
46
+ return delta * LINE_HEIGHT_PX
47
+ }
48
+ if (deltaMode === 2) {
49
+ // DOM_DELTA_PAGE: ページ単位
50
+ return delta * viewportSize
51
+ }
52
+ return delta
53
+ }
54
+
55
+ /**
56
+ * Splits a wheel event into scroll axes, in pixels, applying the package's gesture rules.
57
+ * ホイールイベントを px 単位の軸へ分解し、本パッケージのジェスチャ規則を適用する処理。
58
+ *
59
+ * 規則 (判定順序そのものが仕様):
60
+ *
61
+ * 1. **shift+ホイール**は `deltaX === 0` のとき縦量を横として読む (Windows 系の慣習)。ブラウザが既に
62
+ * `deltaX` へマップ済みなら通常の軸のまま扱う。
63
+ * 2. **横優勢判定**は横成分と縦成分の絶対値比較。等しい場合 (`|h| === |v|`) は横に倒す。ただし
64
+ * **両軸とも 0 のときは横優勢としない** — 慣性の惰性通知で毎 tick 送られる 0/0 を横として
65
+ * 消費すると、ページ側のスクロールを無言で飲み込む。
66
+ *
67
+ * ❗ **これは解決器であって方針ではない。** `isZoomGesture` (ctrl 押下) が true でもデルタは
68
+ * そのまま返す。「ズームは横取りしない」という**方針**は適用側 (`ScrollPane.applyWheel`) が持つ。
69
+ *
70
+ * @param event - The wheel event / ホイールイベント
71
+ * @param viewportSize - Viewport length for `DOM_DELTA_PAGE` conversion / `DOM_DELTA_PAGE` 変換に使うビューポート長
72
+ * @returns The resolved axes / 解決した軸情報
73
+ */
74
+ export const resolveWheelAxes = (event: WheelEvent, viewportSize: number): ResolvedWheelAxes => {
75
+ const usesShiftAxis = event.shiftKey && event.deltaX === 0
76
+ const rawHorizontal = usesShiftAxis ? event.deltaY : event.deltaX
77
+ const rawVertical = usesShiftAxis ? 0 : event.deltaY
78
+ const isHorizontalDominant = rawHorizontal !== 0 && Math.abs(rawHorizontal) >= Math.abs(rawVertical)
79
+
80
+ return {
81
+ // ❗ ズームでもデルタは潰さない。ここは**事実だけ**を報告し、「横取りしない」という
82
+ // 判断は適用側 (applyWheel) が下す。ここで 0 を詰めると適用側の ctrl 判定が
83
+ // 到達不能な飾りになり、`ctrl+shift+ホイール` を横スクロールとして食う退行を
84
+ // どのテストも検出できなくなる (変異解析で実証済み)。
85
+ isZoomGesture: event.ctrlKey,
86
+ isHorizontalDominant,
87
+ horizontalDelta: toPixelDelta(rawHorizontal, event.deltaMode, viewportSize),
88
+ verticalDelta: toPixelDelta(rawVertical, event.deltaMode, viewportSize),
89
+ }
90
+ }
@@ -0,0 +1,36 @@
1
+ /**
2
+ * @module wheelConsumption
3
+ * @description Tracks which wheel events this package has already applied.
4
+ *
5
+ * @description 本パッケージが既に適用したホイールイベントを記録するモジュール。
6
+ *
7
+ * ❗ **`event.defaultPrevented` では代用できない。** あれは「誰かがこのイベントを止めた」であって
8
+ * 「**我々が**消費した」ではない。両者を混同すると、ページ全体のスクロールロック
9
+ * (`document` に capture + `preventDefault` を張る定番の実装) を掛けただけで一覧が
10
+ * まったくスクロールできなくなる。逆に印が無いと、ペインの入れ子や祖先に張った `useWheelBridge` で
11
+ * **同じデルタが 2 回適用**される (実測: 内側と外側の一覧が 1 ノッチで両方 100px 動く)。
12
+ *
13
+ * `WeakSet` なのでイベントオブジェクトの寿命を延ばさない。
14
+ */
15
+
16
+ /** Wheel events already applied by a pane in this page. / このページのペインが適用済みのホイールイベント。 */
17
+ const consumedWheelEvents = new WeakSet<WheelEvent>()
18
+
19
+ /**
20
+ * Marks a wheel event as applied by this package.
21
+ * ホイールイベントを「本パッケージが適用済み」として記録する処理。
22
+ *
23
+ * @param event - The wheel event that was applied / 適用したホイールイベント
24
+ */
25
+ export const markWheelConsumed = (event: WheelEvent): void => {
26
+ consumedWheelEvents.add(event)
27
+ }
28
+
29
+ /**
30
+ * Reports whether this package already applied the given wheel event.
31
+ * 指定のホイールイベントを本パッケージが既に適用済みかどうかを返す処理。
32
+ *
33
+ * @param event - The wheel event to test / 判定するホイールイベント
34
+ * @returns True when it was already applied / 既に適用済みの場合に true
35
+ */
36
+ export const wasWheelConsumed = (event: WheelEvent): boolean => consumedWheelEvents.has(event)