@aiquants/virtualscroll 1.22.1 → 1.23.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 +22 -0
- package/README.md +36 -0
- package/dist/ScrollPane.d.cts +24 -0
- package/dist/ScrollPane.d.ts +24 -0
- package/dist/ScrollPane.d.ts.map +1 -1
- package/dist/VirtualScroll.d.cts +25 -0
- package/dist/VirtualScroll.d.ts +25 -0
- package/dist/VirtualScroll.d.ts.map +1 -1
- package/dist/index.cjs +1 -1
- package/dist/index.js +1323 -1319
- package/dist/styles/virtualscroll.standalone.css +1 -1
- package/package.json +1 -1
- package/src/ScrollPane.tsx +30 -2
- package/src/VirtualScroll.tsx +27 -0
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
/*! tailwindcss v4.3.0 | MIT License | https://tailwindcss.com */
|
|
2
2
|
/*! @aiquants/virtualscroll standalone CSS — 非 Tailwind ホスト専用。ホストの Tailwind ビルドと混在させないこと */
|
|
3
|
-
@layer properties{@supports (((-webkit-hyphens:none)) and (not (margin-trim:inline))) or ((-moz-orient:inline) and (not (color:rgb(from red r g b)))){*,:before,:after,::backdrop{--tw-rotate-x:initial;--tw-rotate-y:initial;--tw-rotate-z:initial;--tw-skew-x:initial;--tw-skew-y:initial;--tw-border-style:solid;--tw-blur:initial;--tw-brightness:initial;--tw-contrast:initial;--tw-grayscale:initial;--tw-hue-rotate:initial;--tw-invert:initial;--tw-opacity:initial;--tw-saturate:initial;--tw-sepia:initial;--tw-drop-shadow:initial;--tw-drop-shadow-color:initial;--tw-drop-shadow-alpha:100%;--tw-drop-shadow-size:initial;--tw-ease:initial}}}@layer theme{:root,:host{--spacing:.25rem;--ease-out:cubic-bezier(0, 0, .2, 1);--default-transition-duration:.15s;--default-transition-timing-function:cubic-bezier(.4, 0, .2, 1)}}@layer base;@layer components{.aqvs-scroll-pane{display:flex;position:relative}.aqvs-scroll-pane-content{flex:1;height:100%;position:relative;overflow:hidden}.aqvs-tap-scroll-circle{touch-action:none;-webkit-user-select:none;user-select:none;justify-content:center;align-items:center;transition-property:transform;transition-duration:.1s;transition-timing-function:cubic-bezier(0,0,.2,1);display:flex;position:relative}.aqvs-tap-scroll-circle-visual-outer{border-radius:9999px;position:absolute;inset:0}.aqvs-tap-scroll-circle-gradient{background:linear-gradient(to bottom right,#1d4ed899,#60a5fa8c,#bfdbfe66);border-width:1px;border-color:#fff6;border-radius:9999px;position:absolute;inset:0;box-shadow:0 4px 6px -1px #0000001a,0 2px 4px -1px #0000000f}.aqvs-tap-scroll-circle-inner{border-radius:9999px;position:absolute;inset:18%}.aqvs-sample-visual-pupil{background-color:#ffffffd9;border-width:1px;border-color:#ffffff80;border-radius:9999px;position:absolute;top:50%;left:50%}.aqvs-sample-visual-highlight{background-color:#fffc;border-radius:9999px;position:absolute;top:50%;left:50%}.aqvs-sample-visual-rod{background-color:#ffffff80;border-radius:9999px;position:absolute;top:50%;left:50%}.aqvs-scrollbar{z-index:50;cursor:default;-webkit-user-select:none;user-select:none;touch-action:none;background-color:#fff;position:relative}.aqvs-scrollbar-horizontal{flex-direction:row;align-items:stretch;display:flex}.aqvs-scrollbar-vertical{flex-direction:column;align-items:stretch;display:flex}.aqvs-scrollbar-tap-circle-wrapper{pointer-events:auto;transition-property:opacity;transition-duration:.15s;transition-timing-function:cubic-bezier(.4,0,.2,1);position:absolute}.aqvs-scrollbar-arrow-button{color:#313131;background-color:#e0e0e0;justify-content:center;align-items:center;font-size:.75rem;line-height:1rem;transition-property:color,background-color,border-color,text-decoration-color,fill,stroke;transition-duration:.15s;transition-timing-function:cubic-bezier(.4,0,.2,1);display:flex}.aqvs-scrollbar-arrow-button:focus{outline-offset:2px;outline:2px solid #0000}.aqvs-scrollbar-arrow-button:focus-visible{outline-offset:1px;outline:2px solid #60a5fa}.aqvs-scrollbar-arrow-button:disabled{cursor:not-allowed;opacity:.5}.aqvs-scrollbar-arrow-button:enabled:hover{background-color:#d4d4d4}.aqvs-scrollbar-track{background-color:#f5f5f5;flex:1;position:relative}.aqvs-scrollbar-overlay{pointer-events:none;position:absolute;inset:0}.aqvs-scrollbar-thumb-wrapper{touch-action:none;position:absolute}.aqvs-scrollbar-thumb{transform-origin:50%;background-color:#7f7f7f;transition:transform 80ms ease-out;position:absolute}.aqvs-scrollbar-thumb[data-thumb-state=disabled]{transition:none;transform:none}.aqvs-scrollbar-thumb[data-thumb-state=hover]{background-color:#5f5f5f}.aqvs-scrollbar-thumb[data-thumb-state=dragging]{background-color:#4f4f4f;transition:transform 60ms ease-out}.aqvs-scrollbar-thumb-horizontal{inset:1.5px 0}.aqvs-scrollbar-thumb-horizontal[data-thumb-state=hover]{top:-.5px;bottom:-.5px;transform:scaleY(1.06)}.aqvs-scrollbar-thumb-horizontal[data-thumb-state=dragging]{top:-.5px;bottom:-.5px;transform:scaleY(1.12)}.aqvs-scrollbar-thumb-vertical{inset:0 1.5px}.aqvs-scrollbar-thumb-vertical[data-thumb-state=hover]{left:-.5px;right:-.5px;transform:scaleX(1.06)}.aqvs-scrollbar-thumb-vertical[data-thumb-state=dragging]{left:-.5px;right:-.5px;transform:scaleX(1.12)}.aqvs-scroll-to-edge-button{pointer-events:auto;color:#fff;text-transform:uppercase;letter-spacing:.05em;-webkit-backdrop-filter:blur(4px);backdrop-filter:blur(4px);background-color:#1f2937cc;border-radius:9999px;padding:.25rem 3rem;font-size:10px;font-weight:500;transition-property:transform,background-color;transition-duration:.15s;transform:none;box-shadow:0 4px 6px -1px #0000001a,0 2px 4px -1px #0000000f}.aqvs-scroll-to-edge-button:hover{background-color:#374151}.aqvs-scroll-to-edge-button:active{transform:scale(.95)}.aqvs-scroll-to-edge-overlay{pointer-events:none;z-index:10;transition-property:opacity;transition-duration:.5s;position:absolute;inset:0}.aqvs-scroll-to-edge-overlay[data-visible=true]{opacity:1}.aqvs-scroll-to-edge-overlay[data-visible=false]{opacity:0}.aqvs-scroll-to-edge-button-container{justify-content:center;display:flex;position:absolute;left:0;right:0}.aqvs-scroll-to-edge-button-container-top{top:.5rem}.aqvs-scroll-to-edge-button-container-bottom{bottom:.5rem}.aqvs-no-items-container{width:100%;position:absolute;top:0}.aqvs-no-items-text{text-align:center;color:#6b7280}.aqvs-item-container,.aqvs-bottom-inset,.aqvs-items-wrapper{width:100%;position:absolute}}@layer utilities{.pointer-events-none{pointer-events:none}.collapse{visibility:collapse}.visible{visibility:visible}.fixed{position:fixed}.relative{position:relative}.static{position:static}.container{width:100%}@media (min-width:40rem){.container{max-width:40rem}}@media (min-width:48rem){.container{max-width:48rem}}@media (min-width:64rem){.container{max-width:64rem}}@media (min-width:80rem){.container{max-width:80rem}}@media (min-width:96rem){.container{max-width:96rem}}.hidden{display:none}.inline{display:inline}.size-1{width:calc(var(--spacing) * 1);height:calc(var(--spacing) * 1)}.shrink{flex-shrink:1}.transform{transform:var(--tw-rotate-x,) var(--tw-rotate-y,) var(--tw-rotate-z,) var(--tw-skew-x,) var(--tw-skew-y,)}.resize{resize:both}.truncate{text-overflow:ellipsis;white-space:nowrap;overflow:hidden}.border{border-style:var(--tw-border-style);border-width:1px}.opacity-0{opacity:0}.blur{--tw-blur:blur(8px);filter:var(--tw-blur,) var(--tw-brightness,) var(--tw-contrast,) var(--tw-grayscale,) var(--tw-hue-rotate,) var(--tw-invert,) var(--tw-saturate,) var(--tw-sepia,) var(--tw-drop-shadow,)}.transition{transition-property:color,background-color,border-color,outline-color,text-decoration-color,fill,stroke,--tw-gradient-from,--tw-gradient-via,--tw-gradient-to,opacity,box-shadow,transform,translate,scale,rotate,filter,-webkit-backdrop-filter,backdrop-filter,display,content-visibility,overlay,pointer-events;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.ease-out{--tw-ease:var(--ease-out);transition-timing-function:var(--ease-out)}}@property --tw-rotate-x{syntax:"*";inherits:false}@property --tw-rotate-y{syntax:"*";inherits:false}@property --tw-rotate-z{syntax:"*";inherits:false}@property --tw-skew-x{syntax:"*";inherits:false}@property --tw-skew-y{syntax:"*";inherits:false}@property --tw-border-style{syntax:"*";inherits:false;initial-value:solid}@property --tw-blur{syntax:"*";inherits:false}@property --tw-brightness{syntax:"*";inherits:false}@property --tw-contrast{syntax:"*";inherits:false}@property --tw-grayscale{syntax:"*";inherits:false}@property --tw-hue-rotate{syntax:"*";inherits:false}@property --tw-invert{syntax:"*";inherits:false}@property --tw-opacity{syntax:"*";inherits:false}@property --tw-saturate{syntax:"*";inherits:false}@property --tw-sepia{syntax:"*";inherits:false}@property --tw-drop-shadow{syntax:"*";inherits:false}@property --tw-drop-shadow-color{syntax:"*";inherits:false}@property --tw-drop-shadow-alpha{syntax:"<percentage>";inherits:false;initial-value:100%}@property --tw-drop-shadow-size{syntax:"*";inherits:false}@property --tw-ease{syntax:"*";inherits:false}
|
|
3
|
+
@layer properties{@supports (((-webkit-hyphens:none)) and (not (margin-trim:inline))) or ((-moz-orient:inline) and (not (color:rgb(from red r g b)))){*,:before,:after,::backdrop{--tw-rotate-x:initial;--tw-rotate-y:initial;--tw-rotate-z:initial;--tw-skew-x:initial;--tw-skew-y:initial;--tw-border-style:solid;--tw-blur:initial;--tw-brightness:initial;--tw-contrast:initial;--tw-grayscale:initial;--tw-hue-rotate:initial;--tw-invert:initial;--tw-opacity:initial;--tw-saturate:initial;--tw-sepia:initial;--tw-drop-shadow:initial;--tw-drop-shadow-color:initial;--tw-drop-shadow-alpha:100%;--tw-drop-shadow-size:initial;--tw-ease:initial}}}@layer theme{:root,:host{--spacing:.25rem;--ease-out:cubic-bezier(0, 0, .2, 1);--default-transition-duration:.15s;--default-transition-timing-function:cubic-bezier(.4, 0, .2, 1)}}@layer base;@layer components{.aqvs-scroll-pane{display:flex;position:relative}.aqvs-scroll-pane-content{flex:1;height:100%;position:relative;overflow:hidden}.aqvs-tap-scroll-circle{touch-action:none;-webkit-user-select:none;user-select:none;justify-content:center;align-items:center;transition-property:transform;transition-duration:.1s;transition-timing-function:cubic-bezier(0,0,.2,1);display:flex;position:relative}.aqvs-tap-scroll-circle-visual-outer{border-radius:9999px;position:absolute;inset:0}.aqvs-tap-scroll-circle-gradient{background:linear-gradient(to bottom right,#1d4ed899,#60a5fa8c,#bfdbfe66);border-width:1px;border-color:#fff6;border-radius:9999px;position:absolute;inset:0;box-shadow:0 4px 6px -1px #0000001a,0 2px 4px -1px #0000000f}.aqvs-tap-scroll-circle-inner{border-radius:9999px;position:absolute;inset:18%}.aqvs-sample-visual-pupil{background-color:#ffffffd9;border-width:1px;border-color:#ffffff80;border-radius:9999px;position:absolute;top:50%;left:50%}.aqvs-sample-visual-highlight{background-color:#fffc;border-radius:9999px;position:absolute;top:50%;left:50%}.aqvs-sample-visual-rod{background-color:#ffffff80;border-radius:9999px;position:absolute;top:50%;left:50%}.aqvs-scrollbar{z-index:50;cursor:default;-webkit-user-select:none;user-select:none;touch-action:none;background-color:#fff;position:relative}.aqvs-scrollbar-horizontal{flex-direction:row;align-items:stretch;display:flex}.aqvs-scrollbar-vertical{flex-direction:column;align-items:stretch;display:flex}.aqvs-scrollbar-tap-circle-wrapper{pointer-events:auto;transition-property:opacity;transition-duration:.15s;transition-timing-function:cubic-bezier(.4,0,.2,1);position:absolute}.aqvs-scrollbar-arrow-button{color:#313131;background-color:#e0e0e0;justify-content:center;align-items:center;font-size:.75rem;line-height:1rem;transition-property:color,background-color,border-color,text-decoration-color,fill,stroke;transition-duration:.15s;transition-timing-function:cubic-bezier(.4,0,.2,1);display:flex}.aqvs-scrollbar-arrow-button:focus{outline-offset:2px;outline:2px solid #0000}.aqvs-scrollbar-arrow-button:focus-visible{outline-offset:1px;outline:2px solid #60a5fa}.aqvs-scrollbar-arrow-button:disabled{cursor:not-allowed;opacity:.5}.aqvs-scrollbar-arrow-button:enabled:hover{background-color:#d4d4d4}.aqvs-scrollbar-track{background-color:#f5f5f5;flex:1;position:relative}.aqvs-scrollbar-overlay{pointer-events:none;position:absolute;inset:0}.aqvs-scrollbar-thumb-wrapper{touch-action:none;position:absolute}.aqvs-scrollbar-thumb{transform-origin:50%;background-color:#7f7f7f;transition:transform 80ms ease-out;position:absolute}.aqvs-scrollbar-thumb[data-thumb-state=disabled]{transition:none;transform:none}.aqvs-scrollbar-thumb[data-thumb-state=hover]{background-color:#5f5f5f}.aqvs-scrollbar-thumb[data-thumb-state=dragging]{background-color:#4f4f4f;transition:transform 60ms ease-out}.aqvs-scrollbar-thumb-horizontal{inset:1.5px 0}.aqvs-scrollbar-thumb-horizontal[data-thumb-state=hover]{top:-.5px;bottom:-.5px;transform:scaleY(1.06)}.aqvs-scrollbar-thumb-horizontal[data-thumb-state=dragging]{top:-.5px;bottom:-.5px;transform:scaleY(1.12)}.aqvs-scrollbar-thumb-vertical{inset:0 1.5px}.aqvs-scrollbar-thumb-vertical[data-thumb-state=hover]{left:-.5px;right:-.5px;transform:scaleX(1.06)}.aqvs-scrollbar-thumb-vertical[data-thumb-state=dragging]{left:-.5px;right:-.5px;transform:scaleX(1.12)}.aqvs-scroll-to-edge-button{pointer-events:auto;color:#fff;text-transform:uppercase;letter-spacing:.05em;-webkit-backdrop-filter:blur(4px);backdrop-filter:blur(4px);background-color:#1f2937cc;border-radius:9999px;padding:.25rem 3rem;font-size:10px;font-weight:500;transition-property:transform,background-color;transition-duration:.15s;transform:none;box-shadow:0 4px 6px -1px #0000001a,0 2px 4px -1px #0000000f}.aqvs-scroll-to-edge-button:hover{background-color:#374151}.aqvs-scroll-to-edge-button:active{transform:scale(.95)}.aqvs-scroll-to-edge-overlay{pointer-events:none;z-index:10;transition-property:opacity;transition-duration:.5s;position:absolute;inset:0}.aqvs-scroll-to-edge-overlay[data-visible=true]{opacity:1}.aqvs-scroll-to-edge-overlay[data-visible=false]{opacity:0}.aqvs-scroll-to-edge-button-container{justify-content:center;display:flex;position:absolute;left:0;right:0}.aqvs-scroll-to-edge-button-container-top{top:.5rem}.aqvs-scroll-to-edge-button-container-bottom{bottom:.5rem}.aqvs-no-items-container{width:100%;position:absolute;top:0}.aqvs-no-items-text{text-align:center;color:#6b7280}.aqvs-item-container,.aqvs-bottom-inset,.aqvs-items-wrapper{width:100%;position:absolute}}@layer utilities{.pointer-events-none{pointer-events:none}.collapse{visibility:collapse}.visible{visibility:visible}.fixed{position:fixed}.relative{position:relative}.static{position:static}.row-0{grid-row:0}.container{width:100%}@media (min-width:40rem){.container{max-width:40rem}}@media (min-width:48rem){.container{max-width:48rem}}@media (min-width:64rem){.container{max-width:64rem}}@media (min-width:80rem){.container{max-width:80rem}}@media (min-width:96rem){.container{max-width:96rem}}.grid{display:grid}.hidden{display:none}.inline{display:inline}.size-1{width:calc(var(--spacing) * 1);height:calc(var(--spacing) * 1)}.shrink{flex-shrink:1}.transform{transform:var(--tw-rotate-x,) var(--tw-rotate-y,) var(--tw-rotate-z,) var(--tw-skew-x,) var(--tw-skew-y,)}.resize{resize:both}.truncate{text-overflow:ellipsis;white-space:nowrap;overflow:hidden}.border{border-style:var(--tw-border-style);border-width:1px}.opacity-0{opacity:0}.blur{--tw-blur:blur(8px);filter:var(--tw-blur,) var(--tw-brightness,) var(--tw-contrast,) var(--tw-grayscale,) var(--tw-hue-rotate,) var(--tw-invert,) var(--tw-saturate,) var(--tw-sepia,) var(--tw-drop-shadow,)}.transition{transition-property:color,background-color,border-color,outline-color,text-decoration-color,fill,stroke,--tw-gradient-from,--tw-gradient-via,--tw-gradient-to,opacity,box-shadow,transform,translate,scale,rotate,filter,-webkit-backdrop-filter,backdrop-filter,display,content-visibility,overlay,pointer-events;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.ease-out{--tw-ease:var(--ease-out);transition-timing-function:var(--ease-out)}}@property --tw-rotate-x{syntax:"*";inherits:false}@property --tw-rotate-y{syntax:"*";inherits:false}@property --tw-rotate-z{syntax:"*";inherits:false}@property --tw-skew-x{syntax:"*";inherits:false}@property --tw-skew-y{syntax:"*";inherits:false}@property --tw-border-style{syntax:"*";inherits:false;initial-value:solid}@property --tw-blur{syntax:"*";inherits:false}@property --tw-brightness{syntax:"*";inherits:false}@property --tw-contrast{syntax:"*";inherits:false}@property --tw-grayscale{syntax:"*";inherits:false}@property --tw-hue-rotate{syntax:"*";inherits:false}@property --tw-invert{syntax:"*";inherits:false}@property --tw-opacity{syntax:"*";inherits:false}@property --tw-saturate{syntax:"*";inherits:false}@property --tw-sepia{syntax:"*";inherits:false}@property --tw-drop-shadow{syntax:"*";inherits:false}@property --tw-drop-shadow-color{syntax:"*";inherits:false}@property --tw-drop-shadow-alpha{syntax:"<percentage>";inherits:false;initial-value:100%}@property --tw-drop-shadow-size{syntax:"*";inherits:false}@property --tw-ease{syntax:"*";inherits:false}
|
package/package.json
CHANGED
package/src/ScrollPane.tsx
CHANGED
|
@@ -92,6 +92,27 @@ export type ScrollPaneProps = {
|
|
|
92
92
|
renderOverlay?: () => React.ReactNode
|
|
93
93
|
/** Initial scroll position. / 初期のスクロール位置。 */
|
|
94
94
|
initialScrollPosition?: number
|
|
95
|
+
/**
|
|
96
|
+
* ARIA / identity attributes applied to the scrollable CONTENT element — the element that
|
|
97
|
+
* directly owns the rendered items and excludes the scrollbar and overlay chrome.
|
|
98
|
+
*
|
|
99
|
+
* This is the correct host for a composite widget role (`listbox` / `tree` / `grid`): the outer
|
|
100
|
+
* root also contains the scrollbar, whose `role="scrollbar"` would become an invalid owned child
|
|
101
|
+
* of that role. Supplying `id` here also overrides the generated one, so the scrollbar's
|
|
102
|
+
* `aria-controls` keeps pointing at the same element.
|
|
103
|
+
*
|
|
104
|
+
* The type is deliberately narrow: `className`, `style` and `ref` are not expressible, so a
|
|
105
|
+
* consumer cannot break the pane's layout through this seam.
|
|
106
|
+
*
|
|
107
|
+
* スクロール可能な「コンテンツ要素」へ適用する ARIA / 識別属性。行だけを直接所有し、
|
|
108
|
+
* スクロールバーとオーバーレイを含まない要素であり、合成ウィジェットのロール
|
|
109
|
+
* (`listbox` / `tree` / `grid`) を置くべき唯一正しい位置。ルート要素にはスクロールバーも
|
|
110
|
+
* 含まれるため、そちらに置くと `role="scrollbar"` が不正な所有子になる。`id` を渡すと生成 id を
|
|
111
|
+
* 上書きするので、スクロールバーの `aria-controls` も同じ要素を指し続ける。
|
|
112
|
+
*
|
|
113
|
+
* `className` / `style` / `ref` を表現できない型にしてあり、この口からレイアウトを壊せない。
|
|
114
|
+
*/
|
|
115
|
+
contentProps?: React.AriaAttributes & { id?: string; role?: React.AriaRole }
|
|
95
116
|
}
|
|
96
117
|
|
|
97
118
|
export type ScrollPaneInertiaOptions = {
|
|
@@ -226,6 +247,7 @@ export const ScrollPane = forwardRef<ScrollPaneHandle, ScrollPaneProps>(
|
|
|
226
247
|
visibleEndIndex,
|
|
227
248
|
renderOverlay,
|
|
228
249
|
initialScrollPosition = 0,
|
|
250
|
+
contentProps,
|
|
229
251
|
},
|
|
230
252
|
ref,
|
|
231
253
|
) => {
|
|
@@ -661,6 +683,9 @@ export const ScrollPane = forwardRef<ScrollPaneHandle, ScrollPaneProps>(
|
|
|
661
683
|
)
|
|
662
684
|
|
|
663
685
|
const id = useId()
|
|
686
|
+
// コンテンツ要素の id。利用側が contentProps.id を渡した場合はそちらを正とし、
|
|
687
|
+
// ScrollBar の aria-controls も同じ要素を指すようにする
|
|
688
|
+
const contentId = contentProps?.id ?? id
|
|
664
689
|
|
|
665
690
|
const dragStateRef = useRef<DragState>({
|
|
666
691
|
pointerType: "",
|
|
@@ -1228,7 +1253,10 @@ export const ScrollPane = forwardRef<ScrollPaneHandle, ScrollPaneProps>(
|
|
|
1228
1253
|
// (空のペインがページのパンを殺すデッドゾーンになり、そちらの実害の方が大きいため)。
|
|
1229
1254
|
...(enablePointerDrag && isScrollable && pointerDragInputs.some((type) => type === "touch" || type === "pen") ? { touchAction: "none" } : {}),
|
|
1230
1255
|
}}
|
|
1231
|
-
|
|
1256
|
+
// ARIA 属性はコンテンツ要素に載せる。ルート要素にはスクロールバーも含まれるため、
|
|
1257
|
+
// 合成ウィジェットのロールをそちらに置くと不正な所有子が生まれる
|
|
1258
|
+
{...contentProps}
|
|
1259
|
+
id={contentId}>
|
|
1232
1260
|
{background}
|
|
1233
1261
|
{children(scrollPositionRef.current)}
|
|
1234
1262
|
</div>
|
|
@@ -1242,7 +1270,7 @@ export const ScrollPane = forwardRef<ScrollPaneHandle, ScrollPaneProps>(
|
|
|
1242
1270
|
enableTrackClick={enableTrackClick}
|
|
1243
1271
|
enableArrowButtons={enableArrowButtons}
|
|
1244
1272
|
scrollBarWidth={scrollBarWidth}
|
|
1245
|
-
ariaControls={
|
|
1273
|
+
ariaControls={contentId}
|
|
1246
1274
|
tapScrollCircleOptions={tapScrollCircleOptions}
|
|
1247
1275
|
itemCount={itemCount}
|
|
1248
1276
|
renderThumbOverlay={renderThumbOverlay}
|
package/src/VirtualScroll.tsx
CHANGED
|
@@ -139,6 +139,31 @@ export type VirtualScrollProps<T> = {
|
|
|
139
139
|
behaviorOptions?: VirtualScrollBehaviorOptions
|
|
140
140
|
/** Delegates horizontal wheel/trackpad delta to an upstream owner (e.g. a frozen-column grid). / 横ホイール量を上流へ委譲する。 */
|
|
141
141
|
onWheelHorizontal?: (deltaX: number) => void
|
|
142
|
+
/**
|
|
143
|
+
* ARIA / identity attributes applied to the scrollable CONTENT element — the element that
|
|
144
|
+
* directly owns the rendered rows and excludes the scrollbar and overlay chrome.
|
|
145
|
+
*
|
|
146
|
+
* This is where a composite widget role belongs. Putting `role="listbox"` / `"tree"` / `"grid"`
|
|
147
|
+
* on the outer root instead would make the scrollbar (`role="scrollbar"`) an owned child of that
|
|
148
|
+
* role, which ARIA does not allow. Passing `id` here also overrides the generated one, so the
|
|
149
|
+
* scrollbar's `aria-controls` keeps referring to the same element.
|
|
150
|
+
*
|
|
151
|
+
* Intermediate wrappers between the content element and the rows carry no role, no global ARIA
|
|
152
|
+
* attribute and no tabindex, so they are transparent to the owned-element relationship — but
|
|
153
|
+
* only while `behaviorOptions.enableKeyboardNavigation` is `false`, which is what a consumer
|
|
154
|
+
* driving selection through `aria-activedescendant` should be doing anyway.
|
|
155
|
+
*
|
|
156
|
+
* スクロール可能な「コンテンツ要素」へ適用する ARIA / 識別属性。行だけを直接所有し、
|
|
157
|
+
* スクロールバーとオーバーレイを含まないため、合成ウィジェットのロールはここへ置く。
|
|
158
|
+
* ルート要素に置くとスクロールバー (`role="scrollbar"`) が不正な所有子になる。`id` を渡すと
|
|
159
|
+
* 生成 id を上書きし、スクロールバーの `aria-controls` も同じ要素を指し続ける。
|
|
160
|
+
*
|
|
161
|
+
* コンテンツ要素と行の間のラッパーはロール・グローバル ARIA 属性・tabindex のいずれも持たない
|
|
162
|
+
* ため所有関係に対して透過だが、それが成り立つのは
|
|
163
|
+
* `behaviorOptions.enableKeyboardNavigation` が `false` のときに限る (行ラッパーへ
|
|
164
|
+
* `tabIndex={-1}` が付くため)。`aria-activedescendant` 方式の利用側は元より false にすべきである。
|
|
165
|
+
*/
|
|
166
|
+
contentProps?: ScrollPaneProps["contentProps"]
|
|
142
167
|
}
|
|
143
168
|
|
|
144
169
|
/**
|
|
@@ -667,6 +692,7 @@ const VirtualScrollInner = <T,>(
|
|
|
667
692
|
scrollBarOptions,
|
|
668
693
|
behaviorOptions,
|
|
669
694
|
onWheelHorizontal,
|
|
695
|
+
contentProps,
|
|
670
696
|
}: VirtualScrollProps<T>,
|
|
671
697
|
ref: React.Ref<VirtualScrollHandle>,
|
|
672
698
|
) => {
|
|
@@ -1882,6 +1908,7 @@ const VirtualScrollInner = <T,>(
|
|
|
1882
1908
|
wheelSpeedMultiplier={wheelSpeedMultiplier}
|
|
1883
1909
|
onWheelHorizontal={onWheelHorizontal}
|
|
1884
1910
|
contentInsets={resolvedInsets}
|
|
1911
|
+
contentProps={contentProps}
|
|
1885
1912
|
visibleStartIndex={visibleStartIndex}
|
|
1886
1913
|
visibleEndIndex={visibleEndIndex}
|
|
1887
1914
|
renderOverlay={renderOverlay}
|