@aiquants/virtualscroll 3.9.2 → 3.10.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 +90 -0
- package/README.md +95 -29
- package/dist/ScrollBar.d.cts +41 -35
- package/dist/ScrollBar.d.ts +41 -35
- package/dist/ScrollBar.d.ts.map +1 -1
- package/dist/ScrollPane.d.cts +12 -14
- package/dist/ScrollPane.d.ts +12 -14
- package/dist/ScrollPane.d.ts.map +1 -1
- package/dist/VirtualGrid.d.cts +6 -6
- package/dist/VirtualGrid.d.ts +6 -6
- package/dist/VirtualGrid.d.ts.map +1 -1
- package/dist/VirtualScroll.d.cts +72 -23
- package/dist/VirtualScroll.d.ts +72 -23
- package/dist/VirtualScroll.d.ts.map +1 -1
- package/dist/index.cjs +1 -1
- package/dist/index.js +1970 -1950
- package/dist/labels.d.cts +38 -7
- package/dist/labels.d.ts +38 -7
- package/dist/labels.d.ts.map +1 -1
- package/dist/styles/virtualscroll.css +1 -1
- package/dist/styles/virtualscroll.standalone.css +1 -1
- package/dist/utils.d.cts +14 -0
- package/dist/utils.d.ts +14 -0
- package/dist/utils.d.ts.map +1 -1
- package/package.json +5 -2
- package/src/ScrollBar.tsx +91 -49
- package/src/ScrollPane.tsx +12 -14
- package/src/VirtualGrid.tsx +6 -6
- package/src/VirtualScroll.tsx +151 -92
- package/src/labels.ts +58 -7
- package/src/styles/virtualscroll.css +51 -3
- package/src/utils.ts +15 -0
package/src/VirtualGrid.tsx
CHANGED
|
@@ -229,7 +229,7 @@ export type VirtualGridRange = {
|
|
|
229
229
|
totalHeight: number
|
|
230
230
|
}
|
|
231
231
|
|
|
232
|
-
/** Live-region options: the consumer owns announcement wording/locale; the built-in catalog (`locale` / `labels`) covers only the
|
|
232
|
+
/** Live-region options: the consumer owns announcement wording/locale; the built-in catalog (`locale` / `labels`) covers only the eleven chrome strings (the scrollbar names, pills, empty state) — row-parity. / liveRegion オプション: 読み上げの文言とロケールは消費側所有。内蔵カタログ (`locale` / `labels`) はクローム 11 文言 (スクロールバーの名前・ピル・空状態) のみ対象 — 行側と同一方針。 */
|
|
233
233
|
export type VirtualGridLiveRegionOptions = {
|
|
234
234
|
/** Builds the announcement (same string = no re-announce, "" clears). / 読み上げ文言の組み立て (同一文字列 = 再読み上げなし、"" でクリア)。 */
|
|
235
235
|
buildMessage: (range: VirtualGridRange) => string
|
|
@@ -419,11 +419,11 @@ export type VirtualGridProps<T> = {
|
|
|
419
419
|
scrollBarOptions?: VirtualScrollScrollBarOptions
|
|
420
420
|
liveRegion?: VirtualGridLiveRegionOptions
|
|
421
421
|
/**
|
|
422
|
-
* UI chrome locale (default `"en"`), forwarded to the embedded vertical VirtualScroll (its
|
|
423
|
-
*
|
|
424
|
-
* arrows. An unsupported value throws a RangeError at render.
|
|
425
|
-
* UI クロームロケール (既定 `"en"`)。内蔵の縦 VirtualScroll (
|
|
426
|
-
* 空状態)
|
|
422
|
+
* UI chrome locale (default `"en"`), forwarded to the embedded vertical VirtualScroll (its bar's
|
|
423
|
+
* names and the 0-row / all-frozen / degenerate-band empty state) and the horizontal bar (the
|
|
424
|
+
* names of the bar, its thumb and its arrows). An unsupported value throws a RangeError at render.
|
|
425
|
+
* UI クロームロケール (既定 `"en"`)。内蔵の縦 VirtualScroll (バーの名前と 0 行・全行固定・退化帯の
|
|
426
|
+
* 空状態) と横バー (バー・つまみ・矢印の名前) へ転送。非対応値は描画時に RangeError。
|
|
427
427
|
*/
|
|
428
428
|
locale?: VirtualScrollLocale
|
|
429
429
|
/**
|
package/src/VirtualScroll.tsx
CHANGED
|
@@ -4,7 +4,7 @@ import { resolveVirtualScrollLabels, type VirtualScrollLabelOverrides, type Virt
|
|
|
4
4
|
import { Logger } from "./logger.ts"
|
|
5
5
|
import { ScrollPane, type ScrollPaneContentInsets, type ScrollPaneHandle, type ScrollPaneProps } from "./ScrollPane.tsx"
|
|
6
6
|
import { useFenwickMapTree } from "./useFenwickMapTree.ts"
|
|
7
|
-
import { minmax } from "./utils.ts"
|
|
7
|
+
import { keepFocusOnPress, minmax } from "./utils.ts"
|
|
8
8
|
|
|
9
9
|
/**
|
|
10
10
|
* Represents the current state of the rendered range and scroll metrics.
|
|
@@ -196,21 +196,23 @@ export type VirtualScrollScrollBarOptions = {
|
|
|
196
196
|
/** Whether the arrow buttons scroll (default `true`). / 矢印ボタンによるスクロールを許可するかどうか (既定 `true`)。 */
|
|
197
197
|
enableArrowButtons?: boolean
|
|
198
198
|
/**
|
|
199
|
-
* Whether the
|
|
200
|
-
* the
|
|
201
|
-
* the
|
|
202
|
-
*
|
|
203
|
-
*
|
|
204
|
-
*
|
|
205
|
-
*
|
|
199
|
+
* Whether the scroll chrome is the keyboard user's scrolling control (default `true`), or pointer-only because the
|
|
200
|
+
* host scrolls the list by keyboard itself (`false`: roving row focus with Arrow / Page / Home / End).
|
|
201
|
+
* `true`: the scrollbar is a named `role="scrollbar"` with a named `role="slider"` thumb, and its arrows (and the
|
|
202
|
+
* visible scroll-to-edge pills) are Tab stops — the rows are not Tab stops, so without a host keyboard model they are
|
|
203
|
+
* the only scrolling controls a keyboard user can reach with Tab.
|
|
204
|
+
* `false`: the scrollbar and the scroll-to-edge pills become pointer-only, like a native scrollbar: both carry
|
|
205
|
+
* `aria-hidden="true"`, nothing in them is a Tab stop, and a press on them never moves focus (neither onto them nor
|
|
206
|
+
* away from where the host put it); pointer scrolling and the pills' clicks are unchanged. Full contract:
|
|
206
207
|
* `ScrollBarProps["enableArrowButtonTabStops"]`.
|
|
207
|
-
*
|
|
208
|
-
*
|
|
209
|
-
*
|
|
210
|
-
* Tab
|
|
211
|
-
*
|
|
212
|
-
*
|
|
213
|
-
*
|
|
208
|
+
* スクロールの部品がキーボード利用者のスクロール操作部品か (既定 `true`)、ホストが一覧のキーボードスクロールを
|
|
209
|
+
* 自分で持つためポインタ専用か (`false`: 行のロービングフォーカスと矢印 / Page / Home / End) の指定。
|
|
210
|
+
* `true`: スクロールバーは名前付きの `role="scrollbar"` で名前付きの `role="slider"` のつまみを持ち、矢印 (と見えている
|
|
211
|
+
* 端へ戻るピル) は Tab の止まり先 — 行は Tab の止まり先ではないため、ホストのキーボードモデルが無ければ Tab で届く
|
|
212
|
+
* 唯一のスクロール操作部品。
|
|
213
|
+
* `false`: スクロールバーと端へ戻るピルはネイティブのスクロールバーと同じくポインタ専用になる。どちらも
|
|
214
|
+
* `aria-hidden="true"` を持ち、中に Tab の止まり先は無く、押下はフォーカスを動かさない (部品へも、ホストが置いた
|
|
215
|
+
* 場所の外へも)。ポインタのスクロールとピルの click は変わらない。契約の全文は
|
|
214
216
|
* `ScrollBarProps["enableArrowButtonTabStops"]`。
|
|
215
217
|
*/
|
|
216
218
|
enableArrowButtonTabStops?: boolean
|
|
@@ -314,12 +316,12 @@ export type VirtualScrollLiveRegionOptions = {
|
|
|
314
316
|
* Formats the announcement text. Called after the visible range settles; returning the same
|
|
315
317
|
* string as last time leaves the DOM untouched (no re-announcement), returning `""` clears
|
|
316
318
|
* the region. The live-region wording and its language are consumer-owned: the package's
|
|
317
|
-
* built-in catalog (`locale` / `labels`) covers only the
|
|
318
|
-
*
|
|
319
|
+
* built-in catalog (`locale` / `labels`) covers only the eleven chrome strings (the scrollbar's
|
|
320
|
+
* accessible names, the scroll-to-edge pills and the empty state), never announcements.
|
|
319
321
|
* 読み上げ文言を組み立てる。可視範囲が静定した後に呼ばれ、前回と同じ文字列なら DOM を
|
|
320
322
|
* 触らない (再読み上げしない)。`""` を返すとリージョンを空にする。読み上げの文面と言語は
|
|
321
|
-
* 消費側の所有物 — パッケージの内蔵カタログ (`locale` / `labels`) が扱うのはクローム
|
|
322
|
-
* (
|
|
323
|
+
* 消費側の所有物 — パッケージの内蔵カタログ (`locale` / `labels`) が扱うのはクローム 11 文言
|
|
324
|
+
* (スクロールバーのアクセシブルネーム、端スクロールピル、空状態) だけで、読み上げは対象外。
|
|
323
325
|
*
|
|
324
326
|
* @param range - The settled visible range / 静定した可視範囲
|
|
325
327
|
* @returns Announcement text / 読み上げ文言
|
|
@@ -428,11 +430,11 @@ export type VirtualScrollProps<T> = {
|
|
|
428
430
|
*/
|
|
429
431
|
liveRegion?: VirtualScrollLiveRegionOptions
|
|
430
432
|
/**
|
|
431
|
-
* UI chrome locale of the built-in strings (default `"en"`): the scrollbar
|
|
432
|
-
* the scroll-to-edge pills and the empty-state text. Live-region wording is not affected (see
|
|
433
|
+
* UI chrome locale of the built-in strings (default `"en"`): the scrollbar's accessible names
|
|
434
|
+
* (the bar, its thumb and its arrows), the scroll-to-edge pills and the empty-state text. Live-region wording is not affected (see
|
|
433
435
|
* {@link VirtualScrollLiveRegionOptions.format}). An unsupported value throws a RangeError at
|
|
434
436
|
* render; no language negotiation happens.
|
|
435
|
-
* 内蔵文言 (
|
|
437
|
+
* 内蔵文言 (スクロールバーのアクセシブルネーム — バー・つまみ・矢印 —、端スクロールピル、空状態文言) の UI クロームロケール
|
|
436
438
|
* (既定 `"en"`)。ライブリージョンの文言には影響しない ({@link VirtualScrollLiveRegionOptions.format}
|
|
437
439
|
* 参照)。非対応値は描画時に RangeError、言語ネゴシエーションなし。
|
|
438
440
|
*/
|
|
@@ -765,14 +767,25 @@ type ItemsWrapperProps = {
|
|
|
765
767
|
* render anchor is a whole number of device px — for example when every row height is a multiple of a lattice L with
|
|
766
768
|
* L × ratio a whole number (L = 4 px at ratios in quarter steps).
|
|
767
769
|
*
|
|
770
|
+
* The wrapper sits in `.aqvs-items-boundary`, its containing block and a relayout boundary: a positioned box with size and
|
|
771
|
+
* layout containment that is neither a flex nor a grid item, attached to the top, left and right padding edges of the pane
|
|
772
|
+
* content (so the wrapper keeps its position and width) with a height of 0. A step that mounts or unmounts rows therefore
|
|
773
|
+
* lays out from that box instead of from the document root. The rows overflow it unclipped, as ink overflow that is not
|
|
774
|
+
* part of the pane content's scrollable area, and the box takes no hit test of its own.
|
|
775
|
+
*
|
|
768
776
|
* 行ラッパー。`translateY` で動かす合成層 (`will-change: transform`) で、平行移動は描くウィンドウの装置の画素の格子へ
|
|
769
777
|
* `snapEdge` の側で揃える (`snapToDevicePixelGrid`。比は `usePaintingDevicePixelRatio` が描画の中で読むので、自分の
|
|
770
778
|
* ウィンドウの文書に描いた一覧は最初の確定から揃う)。揃えるのは層の平行移動だけ。層の中の行は厳密なレイアウトの位置に
|
|
771
779
|
* 描かれ、角の丸い枠線・輪・輪郭はそこで滲む。行が装置の画素の整数から始まるのは、描画のアンカーからの行の位置が装置 px の
|
|
772
780
|
* 整数のときだけ (例: どの行の高さも、L × 比が整数になる格子 L の倍数のとき。比が 4 分の 1 刻みなら L = 4px)。
|
|
773
781
|
*
|
|
782
|
+
* ラッパーは包含ブロックで配置の境界の `.aqvs-items-boundary` の中に置く。位置指定され、大きさと配置を封じ込め、flex の子でも
|
|
783
|
+
* grid の子でもない箱で、ペインの中身のパディングの上・左・右の辺に付き (ラッパーの位置と幅は変わらない)、高さは 0。行を足し引き
|
|
784
|
+
* する 1 段の配置は、文書の根ではなくこの箱から始まる。行は箱から切り取られずにはみ出し (ペインの中身のスクロールできる範囲に
|
|
785
|
+
* 数えないインクのはみ出し)、箱自身は当たり判定を取らない。
|
|
786
|
+
*
|
|
774
787
|
* @param props - The exact translate, the edge it keeps and the wrapped rows / 厳密な平行移動・守る端・包む行
|
|
775
|
-
* @returns The wrapper element /
|
|
788
|
+
* @returns The boundary box holding the wrapper element / ラッパーの要素を包む境界の箱
|
|
776
789
|
* @throws {Error} When the wrapper attaches to a document without a window (see `usePaintingDevicePixelRatio`) / ウィンドウを持たない文書へ取り付いたとき (`usePaintingDevicePixelRatio` を参照)
|
|
777
790
|
* @throws {RangeError} When the painting window reports a ratio that is not a finite number > 0 (see `snapToDevicePixelGrid`) / 描くウィンドウの比が 0 より大きい有限数でないとき (`snapToDevicePixelGrid` を参照)
|
|
778
791
|
*/
|
|
@@ -785,9 +798,13 @@ const ItemsWrapper = ({ translateY, snapEdge, children }: ItemsWrapperProps) =>
|
|
|
785
798
|
// ❗ 層の様式は will-change: transform だけ。perspective や 3D の変換を足すと Chromium は描画の切り取り (cull rect) を
|
|
786
799
|
// 外して描くが、行を非 2D の変換の下で合成するので画素比によっては層ごと再標本化する (実測: 比 1.25 の明色で選択の輪と
|
|
787
800
|
// フォーカスの輪の間の帯が混ざる)。窓をずらす 1 段がはみ出しを切る中身の行を描き直す費用は、その代わりに受け入れる
|
|
801
|
+
// ❗ ペインの中身は flex の子で配置の境界になれない。境界の箱を挟まないと、行を足し引きする 1 段の配置が毎回文書の根から
|
|
802
|
+
// 始まる (実測: 4 倍の CPU で 1 段の配置の時間のすべてが文書の根から)
|
|
788
803
|
return (
|
|
789
|
-
<div
|
|
790
|
-
{
|
|
804
|
+
<div className="aqvs-items-boundary">
|
|
805
|
+
<div ref={attach} className="aqvs-items-wrapper" style={{ top: 0, transform: `translateY(${wrapperTranslateY}px)`, willChange: "transform" }}>
|
|
806
|
+
{children}
|
|
807
|
+
</div>
|
|
791
808
|
</div>
|
|
792
809
|
)
|
|
793
810
|
}
|
|
@@ -1000,9 +1017,72 @@ const computeRenderingRangesHuge = (effectiveScrollPosition: number, viewportSiz
|
|
|
1000
1017
|
* 高精度タイムスタンプを可能なら取得。
|
|
1001
1018
|
*/
|
|
1002
1019
|
/**
|
|
1003
|
-
*
|
|
1020
|
+
* The first visible row at a logical scroll position (`resolveVisibleStartRow`).
|
|
1021
|
+
*
|
|
1022
|
+
* 論理スクロール位置での先頭の可視行 (`resolveVisibleStartRow`)。
|
|
1023
|
+
*/
|
|
1024
|
+
export type VisibleStartRow = {
|
|
1025
|
+
/** Index of the first visible row / 先頭の可視行の index */
|
|
1026
|
+
readonly index: number
|
|
1027
|
+
/** Logical top of that row (px); `position - top` is the part hidden above the viewport top / その行の論理上端 (px)。`position - top` がビューポートの上端より上に隠れた量 */
|
|
1028
|
+
readonly top: number
|
|
1029
|
+
}
|
|
1030
|
+
|
|
1031
|
+
/**
|
|
1032
|
+
* The part of the Fenwick tree `resolveVisibleStartRow` reads.
|
|
1033
|
+
*
|
|
1034
|
+
* `resolveVisibleStartRow` が読む Fenwick 木の部分。
|
|
1035
|
+
*/
|
|
1036
|
+
type VisibleStartRowTree = Pick<ReturnType<typeof useFenwickMapTree>, "findIndexAtOrAfter" | "prefixSum">
|
|
1037
|
+
|
|
1038
|
+
/** Tree reads that only look the rows up, never materialising them / 行を引くだけで具現化しない木の読み方 */
|
|
1039
|
+
const LOOKUP_ONLY = { materializeOption: { materialize: false } } as const
|
|
1040
|
+
|
|
1041
|
+
/**
|
|
1042
|
+
* Resolves the first visible row at a logical scroll position by the visible-start boundary rule — the one rule that
|
|
1043
|
+
* `scrollTo` pins, `getScrollAnchor` reports, `updateItemSize` compensates above and `computeRenderingRanges` renders from:
|
|
1044
|
+
*
|
|
1045
|
+
* - the row whose span contains the position: `top <= position < top + height`;
|
|
1046
|
+
* - a row whose bottom equals the position lies entirely above the viewport, so the next row starts exactly there and is the
|
|
1047
|
+
* first visible row, at offset 0;
|
|
1048
|
+
* - at the end, the last row stays the first visible row (the end clamp): when the position equals its bottom, and when the
|
|
1049
|
+
* position lies past the end of the content (a transient after the list shrinks, before the pane clamps).
|
|
1050
|
+
*
|
|
1051
|
+
* Module-level export (NOT in the package barrel).
|
|
1052
|
+
*
|
|
1053
|
+
* 先頭の可視行を、可視の先頭の境界の規則で論理スクロール位置から解決する処理。`scrollTo` が留め、`getScrollAnchor` が知らせ、
|
|
1054
|
+
* `updateItemSize` がその上の変化を補正し、`computeRenderingRanges` が描き始める、ただ 1 つの規則。
|
|
1055
|
+
*
|
|
1056
|
+
* - 位置を含む行 (`上端 <= 位置 < 上端 + 高さ`)。
|
|
1057
|
+
* - 下端が位置に一致する行は丸ごとビューポートの上にあるので、ちょうどそこから始まる次の行がオフセット 0 で先頭の可視行。
|
|
1058
|
+
* - 末尾では最後の行が先頭の可視行のまま (末尾のクランプ)。位置がその下端に一致するときと、位置が中身の末尾を越えるとき
|
|
1059
|
+
* (一覧が縮んでからペインがクランプするまでの過渡状態)。
|
|
1060
|
+
*
|
|
1061
|
+
* モジュールレベル export (バレル非公開)。
|
|
1004
1062
|
*
|
|
1005
|
-
*
|
|
1063
|
+
* @param fenwickTree - The row-height tree / 行の高さの木
|
|
1064
|
+
* @param position - Logical scroll position (px, 0 or more) / 論理スクロール位置 (px。0 以上)
|
|
1065
|
+
* @param itemCount - Number of rows, 1 or more / 行の数 (1 以上)
|
|
1066
|
+
* @returns The first visible row and its logical top / 先頭の可視行とその論理上端
|
|
1067
|
+
*/
|
|
1068
|
+
export const resolveVisibleStartRow = (fenwickTree: VisibleStartRowTree, position: number, itemCount: number): VisibleStartRow => {
|
|
1069
|
+
const lastIndex = itemCount - 1
|
|
1070
|
+
const found = fenwickTree.findIndexAtOrAfter(position, LOOKUP_ONLY)
|
|
1071
|
+
if (found.index === -1 || found.index > lastIndex || found.cumulative === undefined || found.currentValue === undefined) {
|
|
1072
|
+
const last = fenwickTree.prefixSum(lastIndex, LOOKUP_ONLY)
|
|
1073
|
+
return { index: lastIndex, top: last.cumulative - last.currentValue }
|
|
1074
|
+
}
|
|
1075
|
+
if (found.cumulative === position && found.index < lastIndex) {
|
|
1076
|
+
return { index: found.index + 1, top: found.cumulative }
|
|
1077
|
+
}
|
|
1078
|
+
return { index: found.index, top: found.cumulative - found.currentValue }
|
|
1079
|
+
}
|
|
1080
|
+
|
|
1081
|
+
/**
|
|
1082
|
+
* Calculates rendering boundaries from current scroll metrics. The visible rows start at the row `resolveVisibleStartRow`
|
|
1083
|
+
* resolves.
|
|
1084
|
+
*
|
|
1085
|
+
* 現在のスクロール情報から描画範囲を算出。可視の行は `resolveVisibleStartRow` が解決する行から始まる。
|
|
1006
1086
|
*/
|
|
1007
1087
|
export const computeRenderingRanges = (scrollPosition: number, viewportSize: number, overscanCount: number, itemSize: number, getItemHeight: (index: number) => number, fenwickTree: ReturnType<typeof useFenwickMapTree>, totalHeight: number) => {
|
|
1008
1088
|
if (itemSize === 0) {
|
|
@@ -1013,31 +1093,9 @@ export const computeRenderingRanges = (scrollPosition: number, viewportSize: num
|
|
|
1013
1093
|
if (itemSize >= Number.MAX_SAFE_INTEGER) {
|
|
1014
1094
|
return computeRenderingRangesHuge(effectiveScrollPosition, viewportSize, overscanCount, itemSize, getItemHeight, fenwickTree, totalHeight, hasFiniteTotal)
|
|
1015
1095
|
}
|
|
1016
|
-
const
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
? (() => {
|
|
1020
|
-
// Fenwick がインデックスを返さない場合は末尾に合わせて開始位置を再計算
|
|
1021
|
-
if (viewportSize <= 0) {
|
|
1022
|
-
return itemSize - 1
|
|
1023
|
-
}
|
|
1024
|
-
return (cumulative ?? 0) < effectiveScrollPosition + (currentValue ?? 0) ? itemSize - 1 : 0
|
|
1025
|
-
})()
|
|
1026
|
-
: rawStartIndex
|
|
1027
|
-
let visibleStartIndex = sanitizeIndex(startIndex, itemSize)
|
|
1028
|
-
|
|
1029
|
-
let visibleHeight = 0
|
|
1030
|
-
if (rawStartIndex !== -1 && cumulative === effectiveScrollPosition) {
|
|
1031
|
-
visibleStartIndex = sanitizeIndex(rawStartIndex + 1, itemSize)
|
|
1032
|
-
visibleHeight = 0
|
|
1033
|
-
} else if (visibleStartIndex === rawStartIndex && cumulative !== undefined && currentValue !== undefined) {
|
|
1034
|
-
const itemTop = cumulative - currentValue
|
|
1035
|
-
visibleHeight = itemTop - effectiveScrollPosition
|
|
1036
|
-
} else {
|
|
1037
|
-
const { cumulative: startCumulative, currentValue: startHeight } = fenwickTree.prefixSum(visibleStartIndex, { materializeOption: { materialize: false } })
|
|
1038
|
-
const itemTop = (startCumulative ?? 0) - (startHeight ?? 0)
|
|
1039
|
-
visibleHeight = itemTop - effectiveScrollPosition
|
|
1040
|
-
}
|
|
1096
|
+
const startRow = resolveVisibleStartRow(fenwickTree, effectiveScrollPosition, itemSize)
|
|
1097
|
+
let visibleStartIndex = startRow.index
|
|
1098
|
+
let visibleHeight = startRow.top - effectiveScrollPosition
|
|
1041
1099
|
|
|
1042
1100
|
const initialOffset = visibleHeight
|
|
1043
1101
|
|
|
@@ -1321,6 +1379,8 @@ const VirtualScrollInner = <T,>(
|
|
|
1321
1379
|
ref: React.Ref<VirtualScrollHandle>,
|
|
1322
1380
|
) => {
|
|
1323
1381
|
const { width: scrollBarWidth, enableThumbDrag, enableTrackClick, enableArrowButtons, enableArrowButtonTabStops, enableScrollToTopBottomButtons, renderThumbOverlay, tapScrollCircleOptions } = scrollBarOptions ?? {}
|
|
1382
|
+
// 合図はスクロールバーと同じ 1 つ (既定 true)。端へ戻るピルもバーと同じくポインタ専用になる
|
|
1383
|
+
const scrollChromeIsPointerOnly = enableArrowButtonTabStops === false
|
|
1324
1384
|
const resolvedLabels = useMemo(() => resolveVirtualScrollLabels(locale, labels), [locale, labels])
|
|
1325
1385
|
|
|
1326
1386
|
const { enablePointerDrag, pointerDragInputs, enableKeyboardNavigation = true, enableEscapeRowReturn = false, wheelSpeedMultiplier, inertiaOptions, overscrollBehavior, clipItemHeight = false, resetOnGetItemHeightChange = false } = behaviorOptions ?? {}
|
|
@@ -1871,9 +1931,9 @@ const VirtualScrollInner = <T,>(
|
|
|
1871
1931
|
const didApplyInitialOffsetRef = useRef(false)
|
|
1872
1932
|
|
|
1873
1933
|
/**
|
|
1874
|
-
*
|
|
1934
|
+
* Applies `initialScrollOffset` once, as the highest-priority initial position, synchronising the pane and the logical position.
|
|
1875
1935
|
*
|
|
1876
|
-
*
|
|
1936
|
+
* `initialScrollOffset` を最優先の初期位置として一度だけ適用し、ペインと論理位置を同期させる effect。
|
|
1877
1937
|
*/
|
|
1878
1938
|
useEffect(() => {
|
|
1879
1939
|
if (didApplyInitialOffsetRef.current) return
|
|
@@ -2006,13 +2066,15 @@ const VirtualScrollInner = <T,>(
|
|
|
2006
2066
|
* Updates the size of a specific item. Contract: after this call, `getItemHeight(index)` must
|
|
2007
2067
|
* return the same `size`; `getItemHeight` is the source of truth, so rows inside the current
|
|
2008
2068
|
* rendering window (including overscan) are otherwise reverted to the `getItemHeight` value by
|
|
2009
|
-
* height reconciliation on the next render. A change above the first visible row
|
|
2069
|
+
* height reconciliation on the next render. A change above the first visible row (the row of the pending
|
|
2070
|
+
* alignment while one is pending, otherwise the row `resolveVisibleStartRow` resolves) moves the scroll
|
|
2010
2071
|
* position by the same delta (layout-shift compensation), carries the remembered alignment along and
|
|
2011
2072
|
* is reported through `onScrollAdjust` (`"item-resize"`) before this returns.
|
|
2012
2073
|
*
|
|
2013
2074
|
* 指定されたアイテムのサイズを更新。契約: 呼び出し後は `getItemHeight(index)` も同じ値を返すこと。
|
|
2014
2075
|
* `getItemHeight` が正であるため、そうでない場合は描画ウィンドウ (オーバースキャン含む) 内の行が
|
|
2015
|
-
* 次レンダーの高さ照合で `getItemHeight`
|
|
2076
|
+
* 次レンダーの高さ照合で `getItemHeight` の値へ巻き戻る。先頭の可視行 (保留中の揃えがあればその行、
|
|
2077
|
+
* 無ければ `resolveVisibleStartRow` が解決する行) より上の変化はスクロール位置を
|
|
2016
2078
|
* 同じ差だけ動かし (レイアウトシフトの補正)、覚えた揃えも一緒に動かして、戻る前に `onScrollAdjust`
|
|
2017
2079
|
* (`"item-resize"`) で知らせる。
|
|
2018
2080
|
*
|
|
@@ -2045,18 +2107,12 @@ const VirtualScrollInner = <T,>(
|
|
|
2045
2107
|
const currentScrollTop = latestScrollPositionRef.current
|
|
2046
2108
|
const insetsTop = normalizeInsets(contentInsets).top
|
|
2047
2109
|
const logicalScrollTop = toLogicalPositionWithInset(currentScrollTop, insetsTop)
|
|
2048
|
-
|
|
2049
|
-
const { index, cumulative } = fenwickTree.findIndexAtOrAfter(logicalScrollTop, { materializeOption: { materialize: false } })
|
|
2050
|
-
// findIndexAtOrAfter は「下端(cumulative) >= scrollTop の最小 index」を返すため、
|
|
2051
|
-
// アイテム index の下端がちょうど scrollTop に一致する場合、その行は完全にビューポート上方にある。
|
|
2052
|
-
// computeRenderingRanges (cumulative === effectiveScrollPosition の特例) と同じく可視先頭を index+1 に補正する。
|
|
2053
|
-
activeVisibleStartIndex = cumulative !== undefined && cumulative === logicalScrollTop ? index + 1 : index
|
|
2110
|
+
activeVisibleStartIndex = resolveVisibleStartRow(fenwickTree, logicalScrollTop, itemCount).index
|
|
2054
2111
|
}
|
|
2055
2112
|
|
|
2056
|
-
// Allow for a small buffer in case of slight misalignments or stale refs
|
|
2057
2113
|
// If the item is strictly above the visible start, it pushes content down.
|
|
2058
|
-
//
|
|
2059
|
-
if (
|
|
2114
|
+
// アイテムが可視領域より上にある場合、コンテンツ全体を押し下げます
|
|
2115
|
+
if (safeIndex < activeVisibleStartIndex && delta !== 0) {
|
|
2060
2116
|
// Use latestScrollPositionRef as the source of truth for the current position
|
|
2061
2117
|
// to avoid reading stale DOM values during batched updates (e.g. multiple items resizing at once).
|
|
2062
2118
|
const currentPanePosition = latestScrollPositionRef.current
|
|
@@ -2203,9 +2259,15 @@ const VirtualScrollInner = <T,>(
|
|
|
2203
2259
|
}, [itemCount, scrollToIndex])
|
|
2204
2260
|
|
|
2205
2261
|
/**
|
|
2206
|
-
* Scrolls to a raw offset
|
|
2262
|
+
* Scrolls to a raw logical offset by pinning the first visible row there (`resolveVisibleStartRow`) with the offset of
|
|
2263
|
+
* its top, so the pending alignment is the row `getScrollAnchor` reports: a position on a row boundary pins the row that
|
|
2264
|
+
* starts there at offset 0, never the hidden row above it.
|
|
2207
2265
|
*
|
|
2208
|
-
*
|
|
2266
|
+
* 論理オフセットへのスクロール。その位置の先頭の可視行 (`resolveVisibleStartRow`) を、その上端からのオフセットで留めるので、
|
|
2267
|
+
* 保留中の揃えは `getScrollAnchor` が知らせる行になる。行の境界の位置では、そこから始まる行をオフセット 0 で留め、上に
|
|
2268
|
+
* 隠れた行は留めない。
|
|
2269
|
+
*
|
|
2270
|
+
* @param newPosition - Logical position in px (floored, then clamped to the content) / 論理位置 (px。切り捨ててから中身の範囲へクランプ)
|
|
2209
2271
|
*/
|
|
2210
2272
|
const scrollTo = useCallback(
|
|
2211
2273
|
(newPosition: number) => {
|
|
@@ -2214,43 +2276,33 @@ const VirtualScrollInner = <T,>(
|
|
|
2214
2276
|
}
|
|
2215
2277
|
const total = fenwickTree.getTotal()
|
|
2216
2278
|
const safePosition = minmax(Math.floor(newPosition), 0, total)
|
|
2217
|
-
const
|
|
2218
|
-
|
|
2219
|
-
// Calculate offset relative to item top to ensure precise positioning
|
|
2220
|
-
// itemTop = cumulative - currentValue
|
|
2221
|
-
const itemTop = (cumulative ?? 0) - (currentValue ?? 0)
|
|
2222
|
-
const offset = itemTop - safePosition
|
|
2223
|
-
|
|
2224
|
-
scrollToIndex(index, { offset })
|
|
2279
|
+
const startRow = resolveVisibleStartRow(fenwickTree, safePosition, itemCount)
|
|
2280
|
+
scrollToIndex(startRow.index, { offset: startRow.top - safePosition })
|
|
2225
2281
|
},
|
|
2226
2282
|
[fenwickTree, itemCount, scrollToIndex],
|
|
2227
2283
|
)
|
|
2228
2284
|
|
|
2229
2285
|
/**
|
|
2230
|
-
* Captures the current top-row anchor for exact position restore across remounts
|
|
2231
|
-
*
|
|
2286
|
+
* Captures the current top-row anchor for exact position restore across remounts: the first visible row that
|
|
2287
|
+
* `resolveVisibleStartRow` resolves (the same row `scrollTo` pins) and the px its top is hidden above the viewport top.
|
|
2288
|
+
* Returns `null` while the list is empty.
|
|
2289
|
+
* 再マウント越しの厳密な位置復元のために、現在の先頭可視行アンカーを取得する処理。行は `resolveVisibleStartRow` が解決する
|
|
2290
|
+
* 先頭の可視行 (`scrollTo` が留める行と同じ) で、空の一覧では `null`。
|
|
2232
2291
|
*
|
|
2233
2292
|
* offsetPx は「先頭可視行の上端がビューポート上端より上に隠れている px」(正の値、論理座標)。
|
|
2234
2293
|
* 復元は initialScrollAnchor へそのまま渡す。可視ウィンドウの行高さは描画のたびに実測が
|
|
2235
2294
|
* ツリーへ照合されるため、保存時点のアンカーは常に正確で、生 px と違い再マウント後の
|
|
2236
2295
|
* 推定空間の違いに対して不変 (px 復元は深い位置で別の行に着地する)。
|
|
2296
|
+
*
|
|
2297
|
+
* @returns The anchor, or `null` without rows / アンカー (行が無ければ `null`)
|
|
2237
2298
|
*/
|
|
2238
2299
|
const getScrollAnchor = useCallback((): { index: number; offsetPx: number } | null => {
|
|
2239
2300
|
if (itemCount === 0) {
|
|
2240
2301
|
return null
|
|
2241
2302
|
}
|
|
2242
2303
|
const logical = toLogicalPositionWithInset(latestScrollPositionRef.current, resolvedInsets.top)
|
|
2243
|
-
const
|
|
2244
|
-
|
|
2245
|
-
// 末尾越え (推定総高さより深い位置) は最終行アンカーへ丸める
|
|
2246
|
-
return { index: itemCount - 1, offsetPx: 0 }
|
|
2247
|
-
}
|
|
2248
|
-
if (cumulative === logical) {
|
|
2249
|
-
// 行の下端がちょうどビューポート上端 = 可視先頭は次の行 (可視域計算と同じ境界規約)
|
|
2250
|
-
return { index: minmax(index + 1, 0, itemCount - 1), offsetPx: 0 }
|
|
2251
|
-
}
|
|
2252
|
-
const itemTop = (cumulative ?? 0) - (currentValue ?? 0)
|
|
2253
|
-
return { index, offsetPx: Math.max(0, logical - itemTop) }
|
|
2304
|
+
const startRow = resolveVisibleStartRow(fenwickTree, logical, itemCount)
|
|
2305
|
+
return { index: startRow.index, offsetPx: Math.max(0, logical - startRow.top) }
|
|
2254
2306
|
}, [fenwickTree, itemCount, resolvedInsets.top])
|
|
2255
2307
|
|
|
2256
2308
|
/**
|
|
@@ -2650,6 +2702,12 @@ const VirtualScrollInner = <T,>(
|
|
|
2650
2702
|
* ポインタ軸の下限は CSS 側の `.aqvs-scroll-to-edge-overlay[data-visible="false"]
|
|
2651
2703
|
* .aqvs-scroll-to-edge-button { pointer-events: none }` が担う (配布 CSS 未読込のホストでは
|
|
2652
2704
|
* `inert` が、`inert` 未実装のブラウザでは CSS が、互いの穴を埋める)。
|
|
2705
|
+
*
|
|
2706
|
+
* ホストがキーボードのスクロールを持つ (`scrollBarOptions.enableArrowButtonTabStops: false`) と、ピルはスクロールバーと
|
|
2707
|
+
* 同じくポインタ専用になる。オーバーレイは見えている間も `aria-hidden="true"` で、ピルは Tab 順に入らず、オーバーレイ
|
|
2708
|
+
* が押下の既定動作を取り消すのでピルの押下はフォーカスを動かさない (click は届く)。
|
|
2709
|
+
*
|
|
2710
|
+
* @returns The overlay, or `null` without the pills / オーバーレイ (ピルを出さないなら `null`)
|
|
2653
2711
|
*/
|
|
2654
2712
|
const renderOverlay = useCallback(() => {
|
|
2655
2713
|
if (!enableScrollToTopBottomButtons) {
|
|
@@ -2658,16 +2716,17 @@ const VirtualScrollInner = <T,>(
|
|
|
2658
2716
|
|
|
2659
2717
|
const isVisible = showScrollButtons && scrollDirection !== null
|
|
2660
2718
|
const isTop = scrollDirection === "up"
|
|
2719
|
+
const pillTabIndex = isVisible && !scrollChromeIsPointerOnly ? 0 : -1
|
|
2661
2720
|
|
|
2662
2721
|
return (
|
|
2663
|
-
<div className="aqvs-scroll-to-edge-overlay" data-visible={isVisible} inert={!isVisible}>
|
|
2722
|
+
<div className="aqvs-scroll-to-edge-overlay" data-visible={isVisible} inert={!isVisible} aria-hidden={scrollChromeIsPointerOnly ? true : undefined} onPointerDown={scrollChromeIsPointerOnly ? keepFocusOnPress : undefined}>
|
|
2664
2723
|
{isTop ? (
|
|
2665
2724
|
<div className="aqvs-scroll-to-edge-button-container aqvs-scroll-to-edge-button-container-top">
|
|
2666
2725
|
<button
|
|
2667
2726
|
type="button"
|
|
2668
2727
|
className="aqvs-scroll-to-edge-button"
|
|
2669
2728
|
// 非表示中はタブ順から外す (inert 未実装ブラウザ向けの下限)
|
|
2670
|
-
tabIndex={
|
|
2729
|
+
tabIndex={pillTabIndex}
|
|
2671
2730
|
onClick={(e) => {
|
|
2672
2731
|
e.stopPropagation()
|
|
2673
2732
|
isProgrammaticScrollRef.current = true
|
|
@@ -2683,7 +2742,7 @@ const VirtualScrollInner = <T,>(
|
|
|
2683
2742
|
type="button"
|
|
2684
2743
|
className="aqvs-scroll-to-edge-button"
|
|
2685
2744
|
// 非表示中はタブ順から外す (inert 未実装ブラウザ向けの下限)
|
|
2686
|
-
tabIndex={
|
|
2745
|
+
tabIndex={pillTabIndex}
|
|
2687
2746
|
onClick={(e) => {
|
|
2688
2747
|
e.stopPropagation()
|
|
2689
2748
|
isProgrammaticScrollRef.current = true
|
|
@@ -2696,7 +2755,7 @@ const VirtualScrollInner = <T,>(
|
|
|
2696
2755
|
)}
|
|
2697
2756
|
</div>
|
|
2698
2757
|
)
|
|
2699
|
-
}, [enableScrollToTopBottomButtons, showScrollButtons, scrollDirection, scrollToIndex, itemCount, resolvedLabels])
|
|
2758
|
+
}, [enableScrollToTopBottomButtons, scrollChromeIsPointerOnly, showScrollButtons, scrollDirection, scrollToIndex, itemCount, resolvedLabels])
|
|
2700
2759
|
|
|
2701
2760
|
// 量子化アンカー (fix: LayoutUnit/f32 精度対策)。行 top はコンテンツ絶対座標そのままではなく
|
|
2702
2761
|
// 「絶対座標 - アンカー」で描画し、ラッパー側 translateY にアンカーを足し戻す。
|
package/src/labels.ts
CHANGED
|
@@ -1,14 +1,16 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* @module labels
|
|
3
|
-
* @description Built-in UI chrome label catalog of the package. Covers exactly the
|
|
4
|
-
* the components render on their own: the ScrollBar
|
|
3
|
+
* @description Built-in UI chrome label catalog of the package. Covers exactly the eleven strings
|
|
4
|
+
* the components render on their own: the ScrollBar accessible names (per orientation, the two
|
|
5
|
+
* arrows, the `role="scrollbar"` bar and its `role="slider"` thumb), the VirtualScroll
|
|
5
6
|
* scroll-to-edge pill texts and the empty-state text. Live-region wording stays consumer-owned
|
|
6
7
|
* (`liveRegion.format` / `liveRegion.buildMessage`) and is not part of this catalog. The default
|
|
7
8
|
* locale is `"en"`; `"ja"` is the second locale. No language negotiation happens here: hosts map
|
|
8
9
|
* `navigator.language` (or anything else) to a supported locale themselves.
|
|
9
10
|
*
|
|
10
|
-
* @description パッケージ内蔵の UI クローム文言カタログ。コンポーネント自身が描画する
|
|
11
|
-
* (ScrollBar
|
|
11
|
+
* @description パッケージ内蔵の UI クローム文言カタログ。コンポーネント自身が描画する 11 文言
|
|
12
|
+
* (向きごとの ScrollBar のアクセシブルネーム — 矢印 2 個・`role="scrollbar"` のバー・その `role="slider"` の
|
|
13
|
+
* つまみ —、VirtualScroll の端スクロールピル文言、空状態文言) だけを対象とし、
|
|
12
14
|
* ライブリージョンの文言は利用側の所有 (`liveRegion.format` / `liveRegion.buildMessage`) で
|
|
13
15
|
* 本カタログの対象外。既定ロケールは `"en"`、第 2 ロケールは `"ja"`。言語ネゴシエーションは行わず、
|
|
14
16
|
* `navigator.language` 等から対応ロケールへの対応付けはホスト側の責務。
|
|
@@ -53,6 +55,30 @@ export type VirtualScrollLabels = {
|
|
|
53
55
|
* 横 ScrollBar 終端 (右) 矢印ボタンの aria-label。
|
|
54
56
|
*/
|
|
55
57
|
readonly scrollRight: string
|
|
58
|
+
/**
|
|
59
|
+
* aria-label of the vertical ScrollBar root (`role="scrollbar"`). Assistive technology appends
|
|
60
|
+
* the role ("scroll bar") itself, so the name carries only what tells the two bars apart.
|
|
61
|
+
* 縦 ScrollBar のルート (`role="scrollbar"`) の aria-label。ロール名 (スクロールバー) は支援技術が
|
|
62
|
+
* 付け足すため、名前は 2 本のバーを区別する情報のみ。
|
|
63
|
+
*/
|
|
64
|
+
readonly verticalScrollBar: string
|
|
65
|
+
/**
|
|
66
|
+
* aria-label of the horizontal ScrollBar root (`role="scrollbar"`).
|
|
67
|
+
* 横 ScrollBar のルート (`role="scrollbar"`) の aria-label。
|
|
68
|
+
*/
|
|
69
|
+
readonly horizontalScrollBar: string
|
|
70
|
+
/**
|
|
71
|
+
* aria-label of the vertical ScrollBar thumb (`role="slider"`), whose value is the scroll
|
|
72
|
+
* position.
|
|
73
|
+
* 縦 ScrollBar のつまみ (`role="slider"`) の aria-label。値はスクロール位置。
|
|
74
|
+
*/
|
|
75
|
+
readonly verticalScrollThumb: string
|
|
76
|
+
/**
|
|
77
|
+
* aria-label of the horizontal ScrollBar thumb (`role="slider"`), whose value is the scroll
|
|
78
|
+
* position.
|
|
79
|
+
* 横 ScrollBar のつまみ (`role="slider"`) の aria-label。値はスクロール位置。
|
|
80
|
+
*/
|
|
81
|
+
readonly horizontalScrollThumb: string
|
|
56
82
|
/**
|
|
57
83
|
* Text of the VirtualScroll scroll-to-top pill, shown when
|
|
58
84
|
* `scrollBarOptions.enableScrollToTopBottomButtons` is on.
|
|
@@ -87,15 +113,32 @@ export type VirtualScrollLabelOverrides = { readonly [K in keyof VirtualScrollLa
|
|
|
87
113
|
* overrides are validated against.
|
|
88
114
|
* {@link VirtualScrollLabels} の全キー (カタログ順)。上書き検証に用いる閉じたキー集合。
|
|
89
115
|
*/
|
|
90
|
-
export const VIRTUAL_SCROLL_LABEL_KEYS = [
|
|
116
|
+
export const VIRTUAL_SCROLL_LABEL_KEYS = [
|
|
117
|
+
"scrollUp",
|
|
118
|
+
"scrollDown",
|
|
119
|
+
"scrollLeft",
|
|
120
|
+
"scrollRight",
|
|
121
|
+
"verticalScrollBar",
|
|
122
|
+
"horizontalScrollBar",
|
|
123
|
+
"verticalScrollThumb",
|
|
124
|
+
"horizontalScrollThumb",
|
|
125
|
+
"scrollToTop",
|
|
126
|
+
"scrollToBottom",
|
|
127
|
+
"noItems",
|
|
128
|
+
] as const satisfies readonly (keyof VirtualScrollLabels)[]
|
|
91
129
|
|
|
92
130
|
// satisfies は部分集合しか保証しないため、キー追加時の登録漏れを型で検出する
|
|
93
131
|
const keysCoverEveryLabel: [Exclude<keyof VirtualScrollLabels, (typeof VIRTUAL_SCROLL_LABEL_KEYS)[number]>] extends [never] ? true : never = true
|
|
94
132
|
void keysCoverEveryLabel
|
|
95
133
|
|
|
96
134
|
/**
|
|
97
|
-
* Frozen built-in catalogs per locale. `en`
|
|
98
|
-
*
|
|
135
|
+
* Frozen built-in catalogs per locale. The arrow, pill and empty-state values of `en` are the
|
|
136
|
+
* package's historical English wording. The bar names state only the orientation, because
|
|
137
|
+
* assistive technology already reads the role as "scroll bar"; the thumb names state what the
|
|
138
|
+
* value measures.
|
|
139
|
+
* ロケールごとの凍結済み内蔵カタログ。`en` の矢印・ピル・空状態の値はパッケージ従来の英語文言。
|
|
140
|
+
* バーの名前は向きのみ (支援技術がロールを既に「スクロールバー」と読むため)、つまみの名前は値が
|
|
141
|
+
* 表すもの。
|
|
99
142
|
*/
|
|
100
143
|
export const VIRTUAL_SCROLL_LABEL_CATALOGS: Readonly<Record<VirtualScrollLocale, VirtualScrollLabels>> = Object.freeze({
|
|
101
144
|
en: Object.freeze({
|
|
@@ -103,6 +146,10 @@ export const VIRTUAL_SCROLL_LABEL_CATALOGS: Readonly<Record<VirtualScrollLocale,
|
|
|
103
146
|
scrollDown: "Scroll down",
|
|
104
147
|
scrollLeft: "Scroll left",
|
|
105
148
|
scrollRight: "Scroll right",
|
|
149
|
+
verticalScrollBar: "Vertical",
|
|
150
|
+
horizontalScrollBar: "Horizontal",
|
|
151
|
+
verticalScrollThumb: "Vertical scroll position",
|
|
152
|
+
horizontalScrollThumb: "Horizontal scroll position",
|
|
106
153
|
scrollToTop: "Top",
|
|
107
154
|
scrollToBottom: "Bottom",
|
|
108
155
|
noItems: "No items",
|
|
@@ -112,6 +159,10 @@ export const VIRTUAL_SCROLL_LABEL_CATALOGS: Readonly<Record<VirtualScrollLocale,
|
|
|
112
159
|
scrollDown: "下へスクロール",
|
|
113
160
|
scrollLeft: "左へスクロール",
|
|
114
161
|
scrollRight: "右へスクロール",
|
|
162
|
+
verticalScrollBar: "縦方向",
|
|
163
|
+
horizontalScrollBar: "横方向",
|
|
164
|
+
verticalScrollThumb: "縦スクロール位置",
|
|
165
|
+
horizontalScrollThumb: "横スクロール位置",
|
|
115
166
|
scrollToTop: "先頭へ",
|
|
116
167
|
scrollToBottom: "末尾へ",
|
|
117
168
|
noItems: "項目がありません",
|
|
@@ -329,6 +329,22 @@
|
|
|
329
329
|
width: 100%;
|
|
330
330
|
}
|
|
331
331
|
|
|
332
|
+
/* 行ラッパーの包含ブロックで、配置の境界 (Chromium の relayout boundary)。窓をずらす 1 段が行を足し引きしても、配置はこの箱から
|
|
333
|
+
* 始まって箱の中で終わり、文書の根まで遡らない。境界になるには flex の子でも grid の子でもないことが要る (ペインの中身は flex の子
|
|
334
|
+
* なので、ラッパーの包含ブロックにしても境界にならない)。この箱は位置指定と、大きさと配置の封じ込めで境界になる。
|
|
335
|
+
* 箱はペインの中身のパディングの辺に付いて幅を合わせ、高さは 0 に固定する: 行は箱の外へはみ出して描かれ (描画は封じ込めないので、
|
|
336
|
+
* 切り取りはペインの中身の overflow: hidden のまま)、箱は当たり判定で背景 (background) の前に立たない。行のはみ出しは封じ込めで
|
|
337
|
+
* インクのはみ出しになるので、ペインの中身のスクロールできる範囲にも数えない。そのため、ブラウザが自分で行を見せるスクロール
|
|
338
|
+
* (オーバースキャンの行の中への Tab・ページ内検索) はペインの中身を動かせず、行の箱が外側のスクローラーの外にあればそちらを動かす */
|
|
339
|
+
.aqvs-items-boundary {
|
|
340
|
+
position: absolute;
|
|
341
|
+
top: 0;
|
|
342
|
+
right: 0;
|
|
343
|
+
left: 0;
|
|
344
|
+
height: 0;
|
|
345
|
+
contain: size layout style;
|
|
346
|
+
}
|
|
347
|
+
|
|
332
348
|
.aqvs-items-wrapper {
|
|
333
349
|
position: absolute;
|
|
334
350
|
width: 100%;
|
|
@@ -547,15 +563,47 @@
|
|
|
547
563
|
transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
|
|
548
564
|
}
|
|
549
565
|
|
|
550
|
-
/*
|
|
551
|
-
*
|
|
552
|
-
*
|
|
566
|
+
/* 強制配色 (forced-colors: active) では、ブラウザがシステム色でない地色をどれも Canvas に置き換える (透明度は残す)。地色だけで
|
|
567
|
+
* 描く部品 (文字色も枠線も輪郭も持たない部品) は周りと区別できなくなるので、どれもここでシステム色の描き方を持つ
|
|
568
|
+
* (forcedColors.spec.tsx がこのファイルから地色だけで描く部品を導いて照合する)。
|
|
569
|
+
* - タップスクロールサークル: グラデーションの視覚は平坦になるので、輪郭で形を残す。
|
|
570
|
+
* - バーとトラック: 縁を GrayText の輪郭で描く (バーは矢印とトラックの下地、トラックはつまみが動く範囲)。
|
|
571
|
+
* - つまみ: ネイティブのスクロールバーと同じくシステム色で塗る。状態の規則 (ホバー・ドラッグ・無効) は同じ選択子でここに
|
|
572
|
+
* 言い直す: 詳細度が同じなので、後に置いたこちらが勝つ。forced-color-adjust: none はホストが塗ったシステム色でない地色を
|
|
573
|
+
* Canvas へ消させない (つまみが溝と同じ色になって消えない)。
|
|
574
|
+
* - サンプルのビジュアルの光と棒: 引いた向きと量を示すので CanvasText で塗る。 */
|
|
553
575
|
@media (forced-colors: active) {
|
|
554
576
|
.aqvs-tap-scroll-circle {
|
|
555
577
|
outline: 1px solid CanvasText;
|
|
556
578
|
outline-offset: -1px;
|
|
557
579
|
border-radius: 50%;
|
|
558
580
|
}
|
|
581
|
+
|
|
582
|
+
.aqvs-scrollbar,
|
|
583
|
+
.aqvs-scrollbar-track {
|
|
584
|
+
outline: 1px solid GrayText;
|
|
585
|
+
outline-offset: -1px;
|
|
586
|
+
}
|
|
587
|
+
|
|
588
|
+
.aqvs-scrollbar-thumb {
|
|
589
|
+
forced-color-adjust: none;
|
|
590
|
+
background-color: CanvasText;
|
|
591
|
+
}
|
|
592
|
+
|
|
593
|
+
.aqvs-scrollbar-thumb[data-thumb-state="hover"],
|
|
594
|
+
.aqvs-scrollbar-thumb[data-thumb-state="dragging"] {
|
|
595
|
+
background-color: Highlight;
|
|
596
|
+
}
|
|
597
|
+
|
|
598
|
+
.aqvs-scrollbar-thumb[data-thumb-state="disabled"] {
|
|
599
|
+
background-color: GrayText;
|
|
600
|
+
}
|
|
601
|
+
|
|
602
|
+
.aqvs-sample-visual-highlight,
|
|
603
|
+
.aqvs-sample-visual-rod {
|
|
604
|
+
forced-color-adjust: none;
|
|
605
|
+
background-color: CanvasText;
|
|
606
|
+
}
|
|
559
607
|
}
|
|
560
608
|
|
|
561
609
|
/* 動きを減らす設定 (prefers-reduced-motion: reduce) では、パッケージが動かす部品はどれも遷移もアニメーションも持たず、
|