@aiquants/virtualscroll 3.7.1 → 3.8.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/CHANGELOG.md +127 -39
- package/README.md +73 -5
- package/dist/ScrollBar.d.cts +35 -1
- package/dist/ScrollBar.d.ts +35 -1
- package/dist/ScrollBar.d.ts.map +1 -1
- package/dist/ScrollPane.d.cts +15 -0
- package/dist/ScrollPane.d.ts +15 -0
- package/dist/ScrollPane.d.ts.map +1 -1
- package/dist/VirtualGrid.d.cts +70 -57
- package/dist/VirtualGrid.d.ts +70 -57
- package/dist/VirtualGrid.d.ts.map +1 -1
- package/dist/VirtualScroll.d.cts +59 -2
- package/dist/VirtualScroll.d.ts +59 -2
- package/dist/VirtualScroll.d.ts.map +1 -1
- package/dist/computeTapScrollVelocity.d.cts +3 -3
- package/dist/computeTapScrollVelocity.d.ts +3 -3
- package/dist/index.cjs +1 -1
- package/dist/index.js +1982 -1948
- package/dist/useGridTapScroll.d.cts +11 -11
- package/dist/useGridTapScroll.d.ts +11 -11
- package/dist/useGridTapScroll.d.ts.map +1 -1
- package/package.json +4 -4
- package/src/ScrollBar.tsx +49 -7
- package/src/ScrollPane.tsx +17 -0
- package/src/VirtualGrid.tsx +111 -70
- package/src/VirtualScroll.tsx +167 -5
- package/src/computeTapScrollVelocity.ts +3 -3
- package/src/styles/virtualscroll.css +1 -1
- package/src/useGridTapScroll.ts +12 -12
|
@@ -2,7 +2,7 @@ import { TapScrollAxisSpeedParams } from './computeTapScrollVelocity.cjs';
|
|
|
2
2
|
import { TapScrollCircleDragState, TapScrollCircleHandle } from './TapScrollCircle.cjs';
|
|
3
3
|
/**
|
|
4
4
|
* Default corner-relative offset (px) for BOTH grid tap-circle axes — the reachability arm (R)
|
|
5
|
-
* of the three-arm placement law
|
|
5
|
+
* of the three-arm placement law. Derivation: full speed needs
|
|
6
6
|
* `maxVisualDistance = 240 px` of advancing pull toward the far screen edge, which is a hard
|
|
7
7
|
* stop on touch (`touch-action: none`). With `off = −200` the disc center sits
|
|
8
8
|
* `sbw − off − size/2 = 12 + 200 − 20 = 192 px` inboard of each far root edge, giving a
|
|
@@ -12,11 +12,11 @@ import { TapScrollCircleDragState, TapScrollCircleHandle } from './TapScrollCirc
|
|
|
12
12
|
* nav-zone envelope ≥ ~2/3. Deliberately a LITERAL: it must NOT track a consumer-supplied
|
|
13
13
|
* `maxVisualDistance` (coupling two knobs would make one silently move the other), and
|
|
14
14
|
* shrinking `maxVisualDistance` instead is forbidden (it changes the speed law's `maxDistance`
|
|
15
|
-
* input
|
|
15
|
+
* input, so a pure-axis drag would no longer reproduce the bar's own speed law).
|
|
16
16
|
* グリッドタップサークル両軸のコーナー相対既定オフセット (px) — 3 アーム配置則の到達性アーム
|
|
17
17
|
* (R)。導出: 全速には前進方向へ 240 px の引きが要り、タッチではスクリーン端がハードストップ。
|
|
18
18
|
* −200 で中心は各遠端から 192 px 内側 = 最悪ケース引き分率 0.80 (ナビ帯込み 0.66)。意図的な
|
|
19
|
-
* リテラル — 消費側 `maxVisualDistance` へ連動させない (
|
|
19
|
+
* リテラル — 消費側 `maxVisualDistance` へ連動させない (2 つのノブを連動させると一方が他方を黙って動かす)。
|
|
20
20
|
*/
|
|
21
21
|
export declare const GRID_TAP_CIRCLE_DEFAULT_OFFSET = -200;
|
|
22
22
|
/** Parameters for `useGridTapScroll` (read live through a params ref — identity-stability is not required of callers). / `useGridTapScroll` のパラメータ (params ref 経由で毎フレーム最新を読む — 呼出し側に identity 安定を要求しない)。 */
|
|
@@ -25,9 +25,9 @@ export type UseGridTapScrollParams = {
|
|
|
25
25
|
enabled: boolean;
|
|
26
26
|
/** Shared pull range (= `max(maxVisualDistance, 1)`). / 共有引き範囲 (= `max(maxVisualDistance, 1)`)。 */
|
|
27
27
|
maxDistance: number;
|
|
28
|
-
/** Horizontal speed parameters (
|
|
28
|
+
/** Horizontal speed parameters (Pₓ, built from the scroll-band width and the column count). / 横軸速度パラメータ (Pₓ。スクロール帯幅と列数から構成)。 */
|
|
29
29
|
xSpeedParams: TapScrollAxisSpeedParams;
|
|
30
|
-
/** Vertical speed parameters (
|
|
30
|
+
/** Vertical speed parameters (P_y, built from the embedded pane height and the scroll-row count). / 縦軸速度パラメータ (P_y。埋め込みペイン高とスクロール行数から構成)。 */
|
|
31
31
|
ySpeedParams: TapScrollAxisSpeedParams;
|
|
32
32
|
/** The grid's x apply seam (absolute position in, clamped applied position out). / グリッドの x 適用シーム (絶対位置 → クランプ後位置)。 */
|
|
33
33
|
applyHxRef: {
|
|
@@ -35,21 +35,21 @@ export type UseGridTapScrollParams = {
|
|
|
35
35
|
};
|
|
36
36
|
/** Synchronous-fresh hx read. / hx の同期・最新読み。 */
|
|
37
37
|
getHx: () => number;
|
|
38
|
-
/** The ONE extracted x clamp law (`getMaxHx` SSOT —
|
|
38
|
+
/** The ONE extracted x clamp law (`getMaxHx` SSOT — never re-derived from the grid's tree). / 唯一抽出の x クランプ則 (`getMaxHx` SSOT — グリッドの木から再導出しない)。 */
|
|
39
39
|
getMaxHx: () => number;
|
|
40
|
-
/** y apply seam: wraps `scrollBy(delta)` AND syncs `vyRef.current = applied` before returning (
|
|
40
|
+
/** y apply seam: wraps `scrollBy(delta)` AND syncs `vyRef.current = applied` before returning (NOT a bare handle passthrough — without the sync, x-frame notifications during a diagonal drag carry a stale y). / y 適用シーム: `scrollBy(delta)` をラップし `vyRef.current = applied` を同期してから返す (素のハンドル素通しではない — 同期が無いと対角ドラッグ中の x フレーム通知が stale な y を運ぶ)。 */
|
|
41
41
|
applyVy: (delta: number) => number;
|
|
42
42
|
/** Synchronous-fresh vy read — MUST read the same authority `applyVy` resolves against (the embedded pane's position), never a notification mirror with a second writer (v3.6.1 — a lagging mirror base overstates `actualDelta` and drives the residual negative = yo-yo). / vy の同期・最新読み — `applyVy` が解決するのと同一権威 (埋め込みペイン位置) を読むこと。第 2 の書き手を持つ通知鏡像は禁止 (v3.6.1 — 遅延鏡像基準は `actualDelta` を過大化し残差を負へ落とすヨーヨー)。 */
|
|
43
43
|
getVy: () => number;
|
|
44
44
|
/** y extent read through the embedded handle's freshness channel (never re-derived). / 埋め込みハンドルの鮮度チャネル経由の y 延長 (再導出禁止)。 */
|
|
45
45
|
getMaxVy: () => number;
|
|
46
|
-
/** Pending column anchor — cleared ONLY on frames whose APPLIED hx delta ≠ 0 (
|
|
46
|
+
/** Pending column anchor — cleared ONLY on frames whose APPLIED hx delta ≠ 0 (a request the clamp swallows keeps it). / 保留列アンカー — 適用済み hx デルタ ≠ 0 のフレームのみ解除 (クランプに吸収された要求では残す)。 */
|
|
47
47
|
pendingColAnchorRef: {
|
|
48
48
|
current: unknown;
|
|
49
49
|
};
|
|
50
|
-
/** x extent-growth freshness key (columnWindow / total width —
|
|
50
|
+
/** x extent-growth freshness key (columnWindow / total width — a change re-arms a loop parked at the edge). / x 延長成長の鮮度キー (columnWindow / 総幅 — 変化で端に止まったループを再武装)。 */
|
|
51
51
|
xExtentFreshness: unknown;
|
|
52
|
-
/** y extent-growth freshness key (the vertical range state —
|
|
52
|
+
/** y extent-growth freshness key (the vertical range state — a change re-arms a loop parked at the edge). / y 延長成長の鮮度キー (縦レンジ state — 変化で端に止まったループを再武装)。 */
|
|
53
53
|
yExtentFreshness: unknown;
|
|
54
54
|
};
|
|
55
55
|
/** Result of `useGridTapScroll`. / `useGridTapScroll` の返り値。 */
|
|
@@ -65,7 +65,7 @@ export type UseGridTapScrollResult = {
|
|
|
65
65
|
* Owns the unified tap circle's drag state and the two-axis rAF integration loop.
|
|
66
66
|
* 統合タップサークルのドラッグ状態と 2 軸 rAF 積分ループを所有するフック。
|
|
67
67
|
*
|
|
68
|
-
* Per-frame semantics
|
|
68
|
+
* Per-frame semantics:
|
|
69
69
|
*
|
|
70
70
|
* ```text
|
|
71
71
|
* dt = min(max((t − last)/1000, 0), TAP_SCROLL_MAX_FRAME_DELTA_SECONDS); skip if dt ≤ 0
|
|
@@ -2,7 +2,7 @@ import { TapScrollAxisSpeedParams } from './computeTapScrollVelocity.ts';
|
|
|
2
2
|
import { TapScrollCircleDragState, TapScrollCircleHandle } from './TapScrollCircle.tsx';
|
|
3
3
|
/**
|
|
4
4
|
* Default corner-relative offset (px) for BOTH grid tap-circle axes — the reachability arm (R)
|
|
5
|
-
* of the three-arm placement law
|
|
5
|
+
* of the three-arm placement law. Derivation: full speed needs
|
|
6
6
|
* `maxVisualDistance = 240 px` of advancing pull toward the far screen edge, which is a hard
|
|
7
7
|
* stop on touch (`touch-action: none`). With `off = −200` the disc center sits
|
|
8
8
|
* `sbw − off − size/2 = 12 + 200 − 20 = 192 px` inboard of each far root edge, giving a
|
|
@@ -12,11 +12,11 @@ import { TapScrollCircleDragState, TapScrollCircleHandle } from './TapScrollCirc
|
|
|
12
12
|
* nav-zone envelope ≥ ~2/3. Deliberately a LITERAL: it must NOT track a consumer-supplied
|
|
13
13
|
* `maxVisualDistance` (coupling two knobs would make one silently move the other), and
|
|
14
14
|
* shrinking `maxVisualDistance` instead is forbidden (it changes the speed law's `maxDistance`
|
|
15
|
-
* input
|
|
15
|
+
* input, so a pure-axis drag would no longer reproduce the bar's own speed law).
|
|
16
16
|
* グリッドタップサークル両軸のコーナー相対既定オフセット (px) — 3 アーム配置則の到達性アーム
|
|
17
17
|
* (R)。導出: 全速には前進方向へ 240 px の引きが要り、タッチではスクリーン端がハードストップ。
|
|
18
18
|
* −200 で中心は各遠端から 192 px 内側 = 最悪ケース引き分率 0.80 (ナビ帯込み 0.66)。意図的な
|
|
19
|
-
* リテラル — 消費側 `maxVisualDistance` へ連動させない (
|
|
19
|
+
* リテラル — 消費側 `maxVisualDistance` へ連動させない (2 つのノブを連動させると一方が他方を黙って動かす)。
|
|
20
20
|
*/
|
|
21
21
|
export declare const GRID_TAP_CIRCLE_DEFAULT_OFFSET = -200;
|
|
22
22
|
/** Parameters for `useGridTapScroll` (read live through a params ref — identity-stability is not required of callers). / `useGridTapScroll` のパラメータ (params ref 経由で毎フレーム最新を読む — 呼出し側に identity 安定を要求しない)。 */
|
|
@@ -25,9 +25,9 @@ export type UseGridTapScrollParams = {
|
|
|
25
25
|
enabled: boolean;
|
|
26
26
|
/** Shared pull range (= `max(maxVisualDistance, 1)`). / 共有引き範囲 (= `max(maxVisualDistance, 1)`)。 */
|
|
27
27
|
maxDistance: number;
|
|
28
|
-
/** Horizontal speed parameters (
|
|
28
|
+
/** Horizontal speed parameters (Pₓ, built from the scroll-band width and the column count). / 横軸速度パラメータ (Pₓ。スクロール帯幅と列数から構成)。 */
|
|
29
29
|
xSpeedParams: TapScrollAxisSpeedParams;
|
|
30
|
-
/** Vertical speed parameters (
|
|
30
|
+
/** Vertical speed parameters (P_y, built from the embedded pane height and the scroll-row count). / 縦軸速度パラメータ (P_y。埋め込みペイン高とスクロール行数から構成)。 */
|
|
31
31
|
ySpeedParams: TapScrollAxisSpeedParams;
|
|
32
32
|
/** The grid's x apply seam (absolute position in, clamped applied position out). / グリッドの x 適用シーム (絶対位置 → クランプ後位置)。 */
|
|
33
33
|
applyHxRef: {
|
|
@@ -35,21 +35,21 @@ export type UseGridTapScrollParams = {
|
|
|
35
35
|
};
|
|
36
36
|
/** Synchronous-fresh hx read. / hx の同期・最新読み。 */
|
|
37
37
|
getHx: () => number;
|
|
38
|
-
/** The ONE extracted x clamp law (`getMaxHx` SSOT —
|
|
38
|
+
/** The ONE extracted x clamp law (`getMaxHx` SSOT — never re-derived from the grid's tree). / 唯一抽出の x クランプ則 (`getMaxHx` SSOT — グリッドの木から再導出しない)。 */
|
|
39
39
|
getMaxHx: () => number;
|
|
40
|
-
/** y apply seam: wraps `scrollBy(delta)` AND syncs `vyRef.current = applied` before returning (
|
|
40
|
+
/** y apply seam: wraps `scrollBy(delta)` AND syncs `vyRef.current = applied` before returning (NOT a bare handle passthrough — without the sync, x-frame notifications during a diagonal drag carry a stale y). / y 適用シーム: `scrollBy(delta)` をラップし `vyRef.current = applied` を同期してから返す (素のハンドル素通しではない — 同期が無いと対角ドラッグ中の x フレーム通知が stale な y を運ぶ)。 */
|
|
41
41
|
applyVy: (delta: number) => number;
|
|
42
42
|
/** Synchronous-fresh vy read — MUST read the same authority `applyVy` resolves against (the embedded pane's position), never a notification mirror with a second writer (v3.6.1 — a lagging mirror base overstates `actualDelta` and drives the residual negative = yo-yo). / vy の同期・最新読み — `applyVy` が解決するのと同一権威 (埋め込みペイン位置) を読むこと。第 2 の書き手を持つ通知鏡像は禁止 (v3.6.1 — 遅延鏡像基準は `actualDelta` を過大化し残差を負へ落とすヨーヨー)。 */
|
|
43
43
|
getVy: () => number;
|
|
44
44
|
/** y extent read through the embedded handle's freshness channel (never re-derived). / 埋め込みハンドルの鮮度チャネル経由の y 延長 (再導出禁止)。 */
|
|
45
45
|
getMaxVy: () => number;
|
|
46
|
-
/** Pending column anchor — cleared ONLY on frames whose APPLIED hx delta ≠ 0 (
|
|
46
|
+
/** Pending column anchor — cleared ONLY on frames whose APPLIED hx delta ≠ 0 (a request the clamp swallows keeps it). / 保留列アンカー — 適用済み hx デルタ ≠ 0 のフレームのみ解除 (クランプに吸収された要求では残す)。 */
|
|
47
47
|
pendingColAnchorRef: {
|
|
48
48
|
current: unknown;
|
|
49
49
|
};
|
|
50
|
-
/** x extent-growth freshness key (columnWindow / total width —
|
|
50
|
+
/** x extent-growth freshness key (columnWindow / total width — a change re-arms a loop parked at the edge). / x 延長成長の鮮度キー (columnWindow / 総幅 — 変化で端に止まったループを再武装)。 */
|
|
51
51
|
xExtentFreshness: unknown;
|
|
52
|
-
/** y extent-growth freshness key (the vertical range state —
|
|
52
|
+
/** y extent-growth freshness key (the vertical range state — a change re-arms a loop parked at the edge). / y 延長成長の鮮度キー (縦レンジ state — 変化で端に止まったループを再武装)。 */
|
|
53
53
|
yExtentFreshness: unknown;
|
|
54
54
|
};
|
|
55
55
|
/** Result of `useGridTapScroll`. / `useGridTapScroll` の返り値。 */
|
|
@@ -65,7 +65,7 @@ export type UseGridTapScrollResult = {
|
|
|
65
65
|
* Owns the unified tap circle's drag state and the two-axis rAF integration loop.
|
|
66
66
|
* 統合タップサークルのドラッグ状態と 2 軸 rAF 積分ループを所有するフック。
|
|
67
67
|
*
|
|
68
|
-
* Per-frame semantics
|
|
68
|
+
* Per-frame semantics:
|
|
69
69
|
*
|
|
70
70
|
* ```text
|
|
71
71
|
* dt = min(max((t − last)/1000, 0), TAP_SCROLL_MAX_FRAME_DELTA_SECONDS); skip if dt ≤ 0
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useGridTapScroll.d.ts","sourceRoot":"","sources":["../src/useGridTapScroll.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAGH,OAAO,EAA4B,KAAK,wBAAwB,EAAE,MAAM,+BAA+B,CAAA;AAEvG,OAAO,KAAK,EAAE,wBAAwB,EAAE,qBAAqB,EAAE,MAAM,uBAAuB,CAAA;AAG5F;;;;;;;;;;;;;;;;;GAiBG;AACH,eAAO,MAAM,8BAA8B,OAAO,CAAA;AAalD,4MAA4M;AAC5M,MAAM,MAAM,sBAAsB,GAAG;IACjC,uIAAuI;IACvI,OAAO,EAAE,OAAO,CAAA;IAChB,mGAAmG;IACnG,WAAW,EAAE,MAAM,CAAA;IACnB,
|
|
1
|
+
{"version":3,"file":"useGridTapScroll.d.ts","sourceRoot":"","sources":["../src/useGridTapScroll.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAGH,OAAO,EAA4B,KAAK,wBAAwB,EAAE,MAAM,+BAA+B,CAAA;AAEvG,OAAO,KAAK,EAAE,wBAAwB,EAAE,qBAAqB,EAAE,MAAM,uBAAuB,CAAA;AAG5F;;;;;;;;;;;;;;;;;GAiBG;AACH,eAAO,MAAM,8BAA8B,OAAO,CAAA;AAalD,4MAA4M;AAC5M,MAAM,MAAM,sBAAsB,GAAG;IACjC,uIAAuI;IACvI,OAAO,EAAE,OAAO,CAAA;IAChB,mGAAmG;IACnG,WAAW,EAAE,MAAM,CAAA;IACnB,gIAAgI;IAChI,YAAY,EAAE,wBAAwB,CAAA;IACtC,6IAA6I;IAC7I,YAAY,EAAE,wBAAwB,CAAA;IACtC,sHAAsH;IACtH,UAAU,EAAE;QAAE,QAAQ,CAAC,OAAO,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,MAAM,CAAA;KAAE,CAAA;IAC1D,gDAAgD;IAChD,KAAK,EAAE,MAAM,MAAM,CAAA;IACnB,mJAAmJ;IACnJ,QAAQ,EAAE,MAAM,MAAM,CAAA;IACtB,oVAAoV;IACpV,OAAO,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,MAAM,CAAA;IAClC,8YAA8Y;IAC9Y,KAAK,EAAE,MAAM,MAAM,CAAA;IACnB,2HAA2H;IAC3H,QAAQ,EAAE,MAAM,MAAM,CAAA;IACtB,mLAAmL;IACnL,mBAAmB,EAAE;QAAE,OAAO,EAAE,OAAO,CAAA;KAAE,CAAA;IACzC,qKAAqK;IACrK,gBAAgB,EAAE,OAAO,CAAA;IACzB,4JAA4J;IAC5J,gBAAgB,EAAE,OAAO,CAAA;CAC5B,CAAA;AAED,+DAA+D;AAC/D,MAAM,MAAM,sBAAsB,GAAG;IACjC,4FAA4F;IAC5F,WAAW,EAAE,OAAO,CAAA;IACpB,qGAAqG;IACrG,yBAAyB,EAAE,CAAC,KAAK,EAAE,wBAAwB,KAAK,IAAI,CAAA;IACpE,4HAA4H;IAC5H,kBAAkB,EAAE,KAAK,CAAC,SAAS,CAAC,qBAAqB,GAAG,IAAI,CAAC,CAAA;CACpE,CAAA;AAED;;;;;;;;;;;;;;;;;;GAkBG;AACH,eAAO,MAAM,gBAAgB,GAAI,QAAQ,sBAAsB,KAAG,sBAuQjE,CAAA"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@aiquants/virtualscroll",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.8.0",
|
|
4
4
|
"description": "High-performance virtual scrolling component for React with variable item heights",
|
|
5
5
|
"sideEffects": [
|
|
6
6
|
"**/*.css"
|
|
@@ -107,8 +107,8 @@
|
|
|
107
107
|
"license-check": "pnpm dlx license-checker --production --onlyAllow \"MIT;Apache-2.0;BSD-2-Clause;BSD-3-Clause;ISC;Unlicense\"",
|
|
108
108
|
"license-check:json": "pnpm dlx license-checker --production --onlyAllow \"MIT;Apache-2.0;BSD-2-Clause;BSD-3-Clause;ISC;Unlicense\" --json",
|
|
109
109
|
"clean": "rimraf dist",
|
|
110
|
-
"publish:patch": "pnpm run typecheck && pnpm run --if-present test && pnpm version patch --no-git-tag-version --no-git-checks && pnpm publish --no-git-checks",
|
|
111
|
-
"publish:minor": "pnpm run typecheck && pnpm run --if-present test && pnpm version minor --no-git-tag-version --no-git-checks && pnpm publish --no-git-checks",
|
|
112
|
-
"publish:major": "pnpm run typecheck && pnpm run --if-present test && pnpm version major --no-git-tag-version --no-git-checks && pnpm publish --no-git-checks"
|
|
110
|
+
"publish:patch": "pnpm run typecheck && pnpm run --if-present test && node ../../.config/scripts/check-publish-leaks.mjs && pnpm version patch --no-git-tag-version --no-git-checks && pnpm publish --no-git-checks",
|
|
111
|
+
"publish:minor": "pnpm run typecheck && pnpm run --if-present test && node ../../.config/scripts/check-publish-leaks.mjs && pnpm version minor --no-git-tag-version --no-git-checks && pnpm publish --no-git-checks",
|
|
112
|
+
"publish:major": "pnpm run typecheck && pnpm run --if-present test && node ../../.config/scripts/check-publish-leaks.mjs && pnpm version major --no-git-tag-version --no-git-checks && pnpm publish --no-git-checks"
|
|
113
113
|
}
|
|
114
114
|
}
|
package/src/ScrollBar.tsx
CHANGED
|
@@ -160,6 +160,40 @@ export type ScrollBarProps = {
|
|
|
160
160
|
enableTrackClick?: boolean
|
|
161
161
|
/** Whether arrow buttons control the scroll position. / 矢印ボタンによるスクロール操作を許可するかどうか。 */
|
|
162
162
|
enableArrowButtons?: boolean
|
|
163
|
+
/**
|
|
164
|
+
* Whether the two arrow buttons are Tab stops (default `true`).
|
|
165
|
+
*
|
|
166
|
+
* `false` renders both with `tabIndex={-1}`, which takes them out of the sequential focus order
|
|
167
|
+
* and changes nothing else: pointer presses and press-and-hold repeat, the accessible names and
|
|
168
|
+
* Enter / Space on an arrow focused from script all keep working. The arrows are descendants of
|
|
169
|
+
* the `role="scrollbar"` root, whose children ARIA 1.2 makes presentational, so whether assistive
|
|
170
|
+
* technology lists them as buttons of their own is the browser's choice (Chromium does); this
|
|
171
|
+
* option changes nothing about that. Set it to `false` when the host already owns keyboard
|
|
172
|
+
* scrolling of this viewport (roving row focus with Arrow / Page / Home / End, a grid keyboard
|
|
173
|
+
* model): the arrows then duplicate that path and cost every keyboard user two extra Tab presses
|
|
174
|
+
* per bar — native scrollbars are never Tab stops either. The default stays `true` because
|
|
175
|
+
* without such a host model the arrows are the only scrolling control a keyboard user can reach
|
|
176
|
+
* with Tab: `ScrollPane` / `VirtualScroll` move their content by transform, so there is no native
|
|
177
|
+
* scroller the browser could make focusable, and their rows are not Tab stops. No effect while
|
|
178
|
+
* the arrows are disabled (`enableArrowButtons: false`, or nothing to scroll) — a disabled button
|
|
179
|
+
* is never focusable.
|
|
180
|
+
*
|
|
181
|
+
* 矢印ボタン 2 個を Tab の止まり先にするかどうか (既定 `true`)。
|
|
182
|
+
*
|
|
183
|
+
* `false` は両方を `tabIndex={-1}` で描画し、順次フォーカス移動の順序から外すだけの指定。
|
|
184
|
+
* ポインタ押下と長押しリピート、アクセシブルネーム、スクリプトからフォーカスした矢印での
|
|
185
|
+
* Enter / Space はすべて維持。矢印は `role="scrollbar"` のルートの子孫で、ARIA 1.2 はその子を
|
|
186
|
+
* presentational とするため、支援技術が矢印を独立したボタンとして示すかどうかはブラウザの選択
|
|
187
|
+
* (Chromium は示す) であり、本オプションはそこに関与しない。ホストがこのビューポートのキーボード
|
|
188
|
+
* スクロールを既に持つ場合 (行のロービングフォーカスと矢印 / Page / Home / End、グリッドのキーボードモデル) に
|
|
189
|
+
* `false` を指定 — 矢印はその経路の重複となり、キーボード利用者にバー 1 本あたり 2 回の余分な Tab を
|
|
190
|
+
* 課すため (ネイティブのスクロールバーも Tab の止まり先にならない)。既定を `true` に据え置く理由は、
|
|
191
|
+
* ホスト側のモデルが無ければ矢印がキーボード利用者の Tab で届く唯一のスクロール操作部品であること
|
|
192
|
+
* (`ScrollPane` / `VirtualScroll` はコンテンツを transform で動かすため、ブラウザがフォーカス可能に
|
|
193
|
+
* できるネイティブのスクロール領域が無く、行も Tab の止まり先ではない)。矢印が無効な間
|
|
194
|
+
* (`enableArrowButtons: false` またはスクロール不要) は効果なし — disabled のボタンはそもそもフォーカス不能。
|
|
195
|
+
*/
|
|
196
|
+
enableArrowButtonTabStops?: boolean
|
|
163
197
|
/** Whether the scrollbar is horizontal. / スクロールバーが水平かどうか。 */
|
|
164
198
|
horizontal?: boolean
|
|
165
199
|
/**
|
|
@@ -611,6 +645,7 @@ export const ScrollBar = ({
|
|
|
611
645
|
enableThumbDrag = true,
|
|
612
646
|
enableTrackClick = true,
|
|
613
647
|
enableArrowButtons = true,
|
|
648
|
+
enableArrowButtonTabStops = true,
|
|
614
649
|
horizontal = false,
|
|
615
650
|
enableHorizontalTapCircle = false,
|
|
616
651
|
stretchMainSize = false,
|
|
@@ -860,7 +895,7 @@ export const ScrollBar = ({
|
|
|
860
895
|
*/
|
|
861
896
|
const stepAutoScroll = useCallback(
|
|
862
897
|
(timestamp: number) => {
|
|
863
|
-
// この rAF ループ意味論は useGridTapScroll (グリッド 2 軸ドライバ) と双子 — 片方を直したらもう片方も直すこと
|
|
898
|
+
// この rAF ループ意味論は useGridTapScroll (グリッド 2 軸ドライバ) と双子 — 片方を直したらもう片方も直すこと
|
|
864
899
|
const state = tapDragStateRef.current
|
|
865
900
|
const metrics = latestMetricsRef.current
|
|
866
901
|
if (!state.active || state.direction === 0) {
|
|
@@ -1436,18 +1471,25 @@ export const ScrollBar = ({
|
|
|
1436
1471
|
}, [tapCircleOffsetX, tapCircleOffsetY, tapCircleSize])
|
|
1437
1472
|
|
|
1438
1473
|
/**
|
|
1439
|
-
* Renders
|
|
1440
|
-
*
|
|
1441
|
-
*
|
|
1474
|
+
* Renders one arrow button of the bar. It is a Tab stop (`tabIndex=0`) unless the host opted out
|
|
1475
|
+
* with `enableArrowButtonTabStops={false}` (`tabIndex=-1`); either way it stays focusable from
|
|
1476
|
+
* script, pointer-operable and named, and Enter / Space scrolls one step while it has focus.
|
|
1477
|
+
*
|
|
1478
|
+
* バーの矢印ボタン 1 個の描画。ホストが `enableArrowButtonTabStops={false}` で外さない限り Tab の
|
|
1479
|
+
* 止まり先 (`tabIndex=0`)、外した場合は `tabIndex=-1`。いずれの場合もスクリプトからのフォーカス・
|
|
1480
|
+
* ポインタ操作・アクセシブルネームは維持し、フォーカス中の Enter / Space で 1 ステップ移動。
|
|
1442
1481
|
*
|
|
1443
|
-
*
|
|
1444
|
-
*
|
|
1482
|
+
* @param direction - Step direction (-1 = toward the start, 1 = toward the end) / ステップ方向 (-1 = 始端側、1 = 終端側)
|
|
1483
|
+
* @param label - Accessible name / アクセシブルネーム
|
|
1484
|
+
* @param icon - Decorative glyph, hidden from assistive technology / 支援技術から隠す装飾グリフ
|
|
1485
|
+
* @param key - React key / React キー
|
|
1486
|
+
* @returns The arrow button element / 矢印ボタン要素
|
|
1445
1487
|
*/
|
|
1446
1488
|
const renderArrowButton = (direction: 1 | -1, label: string, icon: string, key: string) => (
|
|
1447
1489
|
<button
|
|
1448
1490
|
key={key}
|
|
1449
1491
|
type="button"
|
|
1450
|
-
tabIndex={0}
|
|
1492
|
+
tabIndex={enableArrowButtonTabStops ? 0 : -1}
|
|
1451
1493
|
className="aqvs-scrollbar-arrow-button"
|
|
1452
1494
|
style={{
|
|
1453
1495
|
[mainSizeKey]: scrollBarWidth,
|
package/src/ScrollPane.tsx
CHANGED
|
@@ -79,6 +79,21 @@ export type ScrollPaneProps = {
|
|
|
79
79
|
enableTrackClick?: boolean
|
|
80
80
|
/** Whether arrow buttons control the scroll position. / 矢印ボタンのスクロール操作を許可するかどうか。 */
|
|
81
81
|
enableArrowButtons?: boolean
|
|
82
|
+
/**
|
|
83
|
+
* Whether the scrollbar's two arrow buttons are Tab stops (default `true`), forwarded to the
|
|
84
|
+
* pane's ScrollBar. Set it to `false` when the host already provides keyboard scrolling for this
|
|
85
|
+
* pane: the arrows then only add two redundant Tab stops (native scrollbars are never Tab stops
|
|
86
|
+
* either). `false` changes `tabIndex` alone — the arrows stay pointer-operable and named. The
|
|
87
|
+
* default stays `true` because otherwise they are the pane's only scrolling control that Tab
|
|
88
|
+
* reaches. Full contract: `ScrollBarProps["enableArrowButtonTabStops"]`.
|
|
89
|
+
* スクロールバーの矢印ボタン 2 個を Tab の止まり先にするかどうか (既定 `true`)。ペインの ScrollBar へ
|
|
90
|
+
* 転送。ホストがこのペインのキーボードスクロールを既に提供する場合に `false` — 矢印は冗長な Tab の
|
|
91
|
+
* 止まり先を 2 つ足すだけになるため (ネイティブのスクロールバーも Tab の止まり先にならない)。`false` が
|
|
92
|
+
* 変えるのは `tabIndex` だけで、ポインタ操作とアクセシブルネームは維持。
|
|
93
|
+
* 既定が `true` なのは、それ以外ではペインで Tab の届く唯一のスクロール操作部品だから。契約の全文は
|
|
94
|
+
* `ScrollBarProps["enableArrowButtonTabStops"]`。
|
|
95
|
+
*/
|
|
96
|
+
enableArrowButtonTabStops?: boolean
|
|
82
97
|
/** Whether dragging the content area scrolls the pane. / コンテンツ領域のドラッグでスクロールさせるかどうか。 */
|
|
83
98
|
enablePointerDrag?: boolean
|
|
84
99
|
/**
|
|
@@ -327,6 +342,7 @@ export const ScrollPane = forwardRef<ScrollPaneHandle, ScrollPaneProps>(
|
|
|
327
342
|
enableThumbDrag = true,
|
|
328
343
|
enableTrackClick = true,
|
|
329
344
|
enableArrowButtons = true,
|
|
345
|
+
enableArrowButtonTabStops = true,
|
|
330
346
|
enablePointerDrag = true,
|
|
331
347
|
pointerDragInputs = DEFAULT_POINTER_DRAG_INPUTS,
|
|
332
348
|
onScroll,
|
|
@@ -1675,6 +1691,7 @@ export const ScrollPane = forwardRef<ScrollPaneHandle, ScrollPaneProps>(
|
|
|
1675
1691
|
enableThumbDrag={enableThumbDrag}
|
|
1676
1692
|
enableTrackClick={enableTrackClick}
|
|
1677
1693
|
enableArrowButtons={enableArrowButtons}
|
|
1694
|
+
enableArrowButtonTabStops={enableArrowButtonTabStops}
|
|
1678
1695
|
scrollBarWidth={scrollBarWidth}
|
|
1679
1696
|
ariaControls={contentId}
|
|
1680
1697
|
tapScrollCircleOptions={tapScrollCircleOptions}
|