@aiquants/virtualscroll 3.1.0 → 3.4.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 +137 -0
- package/README.md +77 -3
- package/dist/ScrollBar.d.cts +9 -1
- package/dist/ScrollBar.d.ts +9 -1
- package/dist/ScrollBar.d.ts.map +1 -1
- package/dist/ScrollPane.d.cts +13 -0
- package/dist/ScrollPane.d.ts +13 -0
- package/dist/ScrollPane.d.ts.map +1 -1
- package/dist/TapScrollCircle.d.cts +6 -0
- package/dist/TapScrollCircle.d.ts +6 -0
- package/dist/TapScrollCircle.d.ts.map +1 -1
- package/dist/VirtualGrid.d.cts +370 -0
- package/dist/VirtualGrid.d.ts +371 -0
- package/dist/VirtualGrid.d.ts.map +1 -0
- package/dist/VirtualScroll.d.cts +47 -0
- package/dist/VirtualScroll.d.ts +47 -0
- package/dist/VirtualScroll.d.ts.map +1 -1
- package/dist/index.cjs +1 -1
- package/dist/index.d.cts +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2369 -1848
- package/dist/styles/virtualscroll.css +1 -1
- package/dist/styles/virtualscroll.standalone.css +1 -1
- package/dist/useFenwickMapTree.d.cts +7 -4
- package/dist/useFenwickMapTree.d.ts +7 -4
- package/dist/useFenwickMapTree.d.ts.map +1 -1
- package/dist/utils.d.cts +15 -0
- package/dist/utils.d.ts +15 -0
- package/dist/utils.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/ScrollBar.tsx +18 -33
- package/src/ScrollPane.tsx +62 -4
- package/src/TapScrollCircle.tsx +13 -4
- package/src/VirtualGrid.tsx +1486 -0
- package/src/VirtualScroll.tsx +30 -6
- package/src/index.ts +1 -0
- package/src/styles/virtualscroll.css +120 -0
- package/src/useFenwickMapTree.ts +34 -12
- package/src/utils.ts +42 -0
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
/*! tailwindcss v4.3.0 | MIT License | https://tailwindcss.com */
|
|
2
|
-
@layer components{.aqvs-scroll-pane{display:flex;position:relative}.aqvs-scroll-pane[data-self-measured=true]{height:100%}.aqvs-scroll-pane-content{box-sizing:border-box;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:1px solid #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{box-sizing:border-box;background-color:#ffffffd9;border:1px solid #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;-webkit-tap-highlight-color:transparent;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[data-visible=false],.aqvs-scrollbar-thumb-wrapper[data-visible=false]{opacity:0;pointer-events:none}.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;letter-spacing:normal;background-color:#e0e0e0;border:0 solid;justify-content:center;align-items:center;padding:0;font-family:system-ui,sans-serif;font-size:.75rem;font-weight:400;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;cursor:pointer;-webkit-user-select:none;user-select:none;-webkit-tap-highlight-color:transparent;color:#fff;text-transform:uppercase;letter-spacing:.05em;-webkit-backdrop-filter:blur(4px);backdrop-filter:blur(4px);background-color:#1f2937cc;border:0 solid;border-radius:9999px;padding:.25rem 3rem;font-family:inherit;font-size:10px;font-weight:500;line-height:1.5;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:focus{outline-offset:2px;outline:2px solid #0000}.aqvs-scroll-to-edge-button:focus-visible{outline-offset:1px;outline:2px solid #60a5fa}.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-overlay[data-visible=false] .aqvs-scroll-to-edge-button{pointer-events:none}.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}.aqvs-live-region{clip-path:inset(50%);white-space:nowrap;border:0;width:1px;height:1px;margin:-1px;padding:0;position:absolute;overflow:hidden}}
|
|
2
|
+
@layer components{.aqvs-scroll-pane{display:flex;position:relative}.aqvs-scroll-pane[data-self-measured=true]{height:100%}.aqvs-scroll-pane-content{box-sizing:border-box;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:1px solid #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{box-sizing:border-box;background-color:#ffffffd9;border:1px solid #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;-webkit-tap-highlight-color:transparent;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[data-visible=false],.aqvs-scrollbar-thumb-wrapper[data-visible=false]{opacity:0;pointer-events:none}.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;letter-spacing:normal;background-color:#e0e0e0;border:0 solid;justify-content:center;align-items:center;padding:0;font-family:system-ui,sans-serif;font-size:.75rem;font-weight:400;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;cursor:pointer;-webkit-user-select:none;user-select:none;-webkit-tap-highlight-color:transparent;color:#fff;text-transform:uppercase;letter-spacing:.05em;-webkit-backdrop-filter:blur(4px);backdrop-filter:blur(4px);background-color:#1f2937cc;border:0 solid;border-radius:9999px;padding:.25rem 3rem;font-family:inherit;font-size:10px;font-weight:500;line-height:1.5;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:focus{outline-offset:2px;outline:2px solid #0000}.aqvs-scroll-to-edge-button:focus-visible{outline-offset:1px;outline:2px solid #60a5fa}.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-overlay[data-visible=false] .aqvs-scroll-to-edge-button{pointer-events:none}.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}.aqvs-live-region{clip-path:inset(50%);white-space:nowrap;border:0;width:1px;height:1px;margin:-1px;padding:0;position:absolute;overflow:hidden}.aqvs-grid{grid-template-rows:minmax(0,1fr) auto;width:100%;height:100%;display:grid;position:relative;overflow:hidden}.aqvs-grid-has-frozen-rows{grid-template-rows:auto minmax(0,1fr) auto}.aqvs-grid-frozen-rows{min-width:0;position:relative;overflow:hidden}.aqvs-grid-frozen-band-row{position:absolute;left:0;overflow:hidden}.aqvs-grid-main{min-width:0;min-height:0;position:relative}.aqvs-grid-row{width:100%;height:100%;transform:translateX(var(--aqvs-grid-hx-residual,0px));position:relative;overflow:visible}.aqvs-grid-cell{height:100%;position:absolute;top:0;overflow:hidden}.aqvs-grid-row.aqvs-grid-row-frozen-host{transform:none}.aqvs-grid-row-scroll{height:100%;top:0;left:var(--aqvs-grid-frozen-width,0px);position:absolute;right:0;overflow:hidden}.aqvs-grid-row-scroll-inner{width:100%;height:100%;transform:translateX(calc(var(--aqvs-grid-hx-residual,0px) - var(--aqvs-grid-frozen-width,0px)));position:absolute;top:0;left:0}.aqvs-grid-hbar-strip{min-width:0;padding-left:var(--aqvs-grid-frozen-width,0px);position:relative}.aqvs-grid-live-region{clip:rect(0 0 0 0);white-space:nowrap;border:0;width:1px;height:1px;margin:-1px;padding:0;position:absolute;overflow:hidden}}
|
|
@@ -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 theme,base;@layer components{.aqvs-scroll-pane{display:flex;position:relative}.aqvs-scroll-pane[data-self-measured=true]{height:100%}.aqvs-scroll-pane-content{box-sizing:border-box;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:1px solid #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{box-sizing:border-box;background-color:#ffffffd9;border:1px solid #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;-webkit-tap-highlight-color:transparent;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[data-visible=false],.aqvs-scrollbar-thumb-wrapper[data-visible=false]{opacity:0;pointer-events:none}.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;letter-spacing:normal;background-color:#e0e0e0;border:0 solid;justify-content:center;align-items:center;padding:0;font-family:system-ui,sans-serif;font-size:.75rem;font-weight:400;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;cursor:pointer;-webkit-user-select:none;user-select:none;-webkit-tap-highlight-color:transparent;color:#fff;text-transform:uppercase;letter-spacing:.05em;-webkit-backdrop-filter:blur(4px);backdrop-filter:blur(4px);background-color:#1f2937cc;border:0 solid;border-radius:9999px;padding:.25rem 3rem;font-family:inherit;font-size:10px;font-weight:500;line-height:1.5;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:focus{outline-offset:2px;outline:2px solid #0000}.aqvs-scroll-to-edge-button:focus-visible{outline-offset:1px;outline:2px solid #60a5fa}.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-overlay[data-visible=false] .aqvs-scroll-to-edge-button{pointer-events:none}.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}.aqvs-live-region{clip-path:inset(50%);white-space:nowrap;border:0;width:1px;height:1px;margin:-1px;padding:0;position:absolute;overflow:hidden}}@layer utilities;
|
|
3
|
+
@layer theme,base;@layer components{.aqvs-scroll-pane{display:flex;position:relative}.aqvs-scroll-pane[data-self-measured=true]{height:100%}.aqvs-scroll-pane-content{box-sizing:border-box;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:1px solid #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{box-sizing:border-box;background-color:#ffffffd9;border:1px solid #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;-webkit-tap-highlight-color:transparent;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[data-visible=false],.aqvs-scrollbar-thumb-wrapper[data-visible=false]{opacity:0;pointer-events:none}.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;letter-spacing:normal;background-color:#e0e0e0;border:0 solid;justify-content:center;align-items:center;padding:0;font-family:system-ui,sans-serif;font-size:.75rem;font-weight:400;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;cursor:pointer;-webkit-user-select:none;user-select:none;-webkit-tap-highlight-color:transparent;color:#fff;text-transform:uppercase;letter-spacing:.05em;-webkit-backdrop-filter:blur(4px);backdrop-filter:blur(4px);background-color:#1f2937cc;border:0 solid;border-radius:9999px;padding:.25rem 3rem;font-family:inherit;font-size:10px;font-weight:500;line-height:1.5;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:focus{outline-offset:2px;outline:2px solid #0000}.aqvs-scroll-to-edge-button:focus-visible{outline-offset:1px;outline:2px solid #60a5fa}.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-overlay[data-visible=false] .aqvs-scroll-to-edge-button{pointer-events:none}.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}.aqvs-live-region{clip-path:inset(50%);white-space:nowrap;border:0;width:1px;height:1px;margin:-1px;padding:0;position:absolute;overflow:hidden}.aqvs-grid{grid-template-rows:minmax(0,1fr) auto;width:100%;height:100%;display:grid;position:relative;overflow:hidden}.aqvs-grid-has-frozen-rows{grid-template-rows:auto minmax(0,1fr) auto}.aqvs-grid-frozen-rows{min-width:0;position:relative;overflow:hidden}.aqvs-grid-frozen-band-row{position:absolute;left:0;overflow:hidden}.aqvs-grid-main{min-width:0;min-height:0;position:relative}.aqvs-grid-row{width:100%;height:100%;transform:translateX(var(--aqvs-grid-hx-residual,0px));position:relative;overflow:visible}.aqvs-grid-cell{height:100%;position:absolute;top:0;overflow:hidden}.aqvs-grid-row.aqvs-grid-row-frozen-host{transform:none}.aqvs-grid-row-scroll{height:100%;top:0;left:var(--aqvs-grid-frozen-width,0px);position:absolute;right:0;overflow:hidden}.aqvs-grid-row-scroll-inner{width:100%;height:100%;transform:translateX(calc(var(--aqvs-grid-hx-residual,0px) - var(--aqvs-grid-frozen-width,0px)));position:absolute;top:0;left:0}.aqvs-grid-hbar-strip{min-width:0;padding-left:var(--aqvs-grid-frozen-width,0px);position:relative}.aqvs-grid-live-region{clip:rect(0 0 0 0);white-space:nowrap;border:0;width:1px;height:1px;margin:-1px;padding:0;position:absolute;overflow:hidden}}@layer utilities;
|
|
@@ -79,7 +79,7 @@ export declare class FenwickMapTree {
|
|
|
79
79
|
* @description Fenwick Tree の初期化。
|
|
80
80
|
* @param {number} size - The total number of items.
|
|
81
81
|
* @param {number | ((index: number) => number)} valueOrFn - The value for all elements, or a function to generate values.
|
|
82
|
-
* @param {{ sampleRange?: { from: number; to: number }, materialize?: boolean }} [options] - Optional settings for initialization.
|
|
82
|
+
* @param {{ sampleRange?: { from: number; to: number }, materialize?: boolean, baseValue?: number }} [options] - Optional settings for initialization (`baseValue` skips sampling — see reset). / 初期化オプション (`baseValue` はサンプリング省略 — reset 参照)。
|
|
83
83
|
*/
|
|
84
84
|
constructor(size: number, valueOrFn: number | ((index: number) => number), options?: {
|
|
85
85
|
sampleRange?: {
|
|
@@ -87,6 +87,7 @@ export declare class FenwickMapTree {
|
|
|
87
87
|
to: number;
|
|
88
88
|
};
|
|
89
89
|
materialize?: boolean;
|
|
90
|
+
baseValue?: number;
|
|
90
91
|
});
|
|
91
92
|
/**
|
|
92
93
|
* @method reset
|
|
@@ -101,7 +102,7 @@ export declare class FenwickMapTree {
|
|
|
101
102
|
* throw しつつ木を半構築で残さず直前の状態を無傷に保つ方針。
|
|
102
103
|
* @param {number} size - The total number of items.
|
|
103
104
|
* @param {number | ((index: number) => number)} valueOrFn - The value for all elements, or a function to generate values.
|
|
104
|
-
* @param {{ sampleRange?: { from: number; to: number }, materialize?: boolean }} [options] - Optional settings for initialization.
|
|
105
|
+
* @param {{ sampleRange?: { from: number; to: number }, materialize?: boolean, baseValue?: number }} [options] - Optional settings for initialization. `baseValue` (finite, >= 0 — RangeError otherwise) skips all sampling and fixes the uniform estimate for unmaterialized items (v3.2.0, the VirtualGrid defaultColWidth path). / 初期化オプション。`baseValue` (有限かつ 0 以上 — 違反は RangeError) は全サンプリングを省略し未具現化アイテムの見積りを固定する (v3.2.0、VirtualGrid defaultColWidth 経路)。
|
|
105
106
|
*/
|
|
106
107
|
reset(size: number, valueOrFn: number | ((index: number) => number), options?: {
|
|
107
108
|
sampleRange?: {
|
|
@@ -109,6 +110,7 @@ export declare class FenwickMapTree {
|
|
|
109
110
|
to: number;
|
|
110
111
|
};
|
|
111
112
|
materialize?: boolean;
|
|
113
|
+
baseValue?: number;
|
|
112
114
|
}): void;
|
|
113
115
|
/**
|
|
114
116
|
* @method setValueFn
|
|
@@ -477,8 +479,8 @@ export declare class FenwickMapTree {
|
|
|
477
479
|
* @param {number} size - The total number of items.
|
|
478
480
|
* @param {number | ((index: number) => number)} valueOrFn - The value for all elements, or a function to generate values.
|
|
479
481
|
* @param {number | ((index: number) => number)} valueOrFn - 全要素の均一な値、または値を生成する関数。不要なツリーの再作成を防ぐため、この関数は `useCallback` でメモ化すること。
|
|
480
|
-
* @param {{ sampleRange?: { from: number; to: number }, resetOnValueFnChange?: boolean, debug?: boolean }} [options] - Optional settings.
|
|
481
|
-
* @param {{ sampleRange?: { from: number; to: number } }} [options] -
|
|
482
|
+
* @param {{ sampleRange?: { from: number; to: number }, resetOnValueFnChange?: boolean, debug?: boolean, baseValue?: number }} [options] - Optional settings (`baseValue` skips sampling — see FenwickMapTree.reset). / オプション (`baseValue` はサンプリング省略 — FenwickMapTree.reset 参照)。
|
|
483
|
+
* @param {{ sampleRange?: { from: number; to: number }, resetOnValueFnChange?: boolean, debug?: boolean, baseValue?: number }} [options] - 初期化時のオプション設定 (`baseValue` はサンプリング省略 — FenwickMapTree.reset 参照)。不要なツリーの再作成を防ぐため、このオブジェクトは `useMemo` でメモ化すること。
|
|
482
484
|
* @returns {FenwickMapTree} The FenwickMapTree instance.
|
|
483
485
|
* @returns {FenwickMapTree} FenwickMapTree インスタンス。
|
|
484
486
|
*/
|
|
@@ -489,5 +491,6 @@ export declare const useFenwickMapTree: (size: number, valueOrFn: number | ((ind
|
|
|
489
491
|
};
|
|
490
492
|
debug?: boolean;
|
|
491
493
|
resetOnValueFnChange?: boolean;
|
|
494
|
+
baseValue?: number;
|
|
492
495
|
}) => FenwickMapTree;
|
|
493
496
|
export {};
|
|
@@ -79,7 +79,7 @@ export declare class FenwickMapTree {
|
|
|
79
79
|
* @description Fenwick Tree の初期化。
|
|
80
80
|
* @param {number} size - The total number of items.
|
|
81
81
|
* @param {number | ((index: number) => number)} valueOrFn - The value for all elements, or a function to generate values.
|
|
82
|
-
* @param {{ sampleRange?: { from: number; to: number }, materialize?: boolean }} [options] - Optional settings for initialization.
|
|
82
|
+
* @param {{ sampleRange?: { from: number; to: number }, materialize?: boolean, baseValue?: number }} [options] - Optional settings for initialization (`baseValue` skips sampling — see reset). / 初期化オプション (`baseValue` はサンプリング省略 — reset 参照)。
|
|
83
83
|
*/
|
|
84
84
|
constructor(size: number, valueOrFn: number | ((index: number) => number), options?: {
|
|
85
85
|
sampleRange?: {
|
|
@@ -87,6 +87,7 @@ export declare class FenwickMapTree {
|
|
|
87
87
|
to: number;
|
|
88
88
|
};
|
|
89
89
|
materialize?: boolean;
|
|
90
|
+
baseValue?: number;
|
|
90
91
|
});
|
|
91
92
|
/**
|
|
92
93
|
* @method reset
|
|
@@ -101,7 +102,7 @@ export declare class FenwickMapTree {
|
|
|
101
102
|
* throw しつつ木を半構築で残さず直前の状態を無傷に保つ方針。
|
|
102
103
|
* @param {number} size - The total number of items.
|
|
103
104
|
* @param {number | ((index: number) => number)} valueOrFn - The value for all elements, or a function to generate values.
|
|
104
|
-
* @param {{ sampleRange?: { from: number; to: number }, materialize?: boolean }} [options] - Optional settings for initialization.
|
|
105
|
+
* @param {{ sampleRange?: { from: number; to: number }, materialize?: boolean, baseValue?: number }} [options] - Optional settings for initialization. `baseValue` (finite, >= 0 — RangeError otherwise) skips all sampling and fixes the uniform estimate for unmaterialized items (v3.2.0, the VirtualGrid defaultColWidth path). / 初期化オプション。`baseValue` (有限かつ 0 以上 — 違反は RangeError) は全サンプリングを省略し未具現化アイテムの見積りを固定する (v3.2.0、VirtualGrid defaultColWidth 経路)。
|
|
105
106
|
*/
|
|
106
107
|
reset(size: number, valueOrFn: number | ((index: number) => number), options?: {
|
|
107
108
|
sampleRange?: {
|
|
@@ -109,6 +110,7 @@ export declare class FenwickMapTree {
|
|
|
109
110
|
to: number;
|
|
110
111
|
};
|
|
111
112
|
materialize?: boolean;
|
|
113
|
+
baseValue?: number;
|
|
112
114
|
}): void;
|
|
113
115
|
/**
|
|
114
116
|
* @method setValueFn
|
|
@@ -477,8 +479,8 @@ export declare class FenwickMapTree {
|
|
|
477
479
|
* @param {number} size - The total number of items.
|
|
478
480
|
* @param {number | ((index: number) => number)} valueOrFn - The value for all elements, or a function to generate values.
|
|
479
481
|
* @param {number | ((index: number) => number)} valueOrFn - 全要素の均一な値、または値を生成する関数。不要なツリーの再作成を防ぐため、この関数は `useCallback` でメモ化すること。
|
|
480
|
-
* @param {{ sampleRange?: { from: number; to: number }, resetOnValueFnChange?: boolean, debug?: boolean }} [options] - Optional settings.
|
|
481
|
-
* @param {{ sampleRange?: { from: number; to: number } }} [options] -
|
|
482
|
+
* @param {{ sampleRange?: { from: number; to: number }, resetOnValueFnChange?: boolean, debug?: boolean, baseValue?: number }} [options] - Optional settings (`baseValue` skips sampling — see FenwickMapTree.reset). / オプション (`baseValue` はサンプリング省略 — FenwickMapTree.reset 参照)。
|
|
483
|
+
* @param {{ sampleRange?: { from: number; to: number }, resetOnValueFnChange?: boolean, debug?: boolean, baseValue?: number }} [options] - 初期化時のオプション設定 (`baseValue` はサンプリング省略 — FenwickMapTree.reset 参照)。不要なツリーの再作成を防ぐため、このオブジェクトは `useMemo` でメモ化すること。
|
|
482
484
|
* @returns {FenwickMapTree} The FenwickMapTree instance.
|
|
483
485
|
* @returns {FenwickMapTree} FenwickMapTree インスタンス。
|
|
484
486
|
*/
|
|
@@ -489,6 +491,7 @@ export declare const useFenwickMapTree: (size: number, valueOrFn: number | ((ind
|
|
|
489
491
|
};
|
|
490
492
|
debug?: boolean;
|
|
491
493
|
resetOnValueFnChange?: boolean;
|
|
494
|
+
baseValue?: number;
|
|
492
495
|
}) => FenwickMapTree;
|
|
493
496
|
export {};
|
|
494
497
|
//# sourceMappingURL=useFenwickMapTree.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useFenwickMapTree.d.ts","sourceRoot":"","sources":["../src/useFenwickMapTree.ts"],"names":[],"mappings":"AA4BA,KAAK,gBAAgB,GAAG;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,EAAE,EAAE,MAAM,CAAA;CAAE,CAAA;AACpD,KAAK,iBAAiB,GAAG;IAAE,WAAW,EAAE,OAAO,CAAC;IAAC,MAAM,CAAC,EAAE,gBAAgB,EAAE,CAAA;CAAE,CAAA;AAC9E,KAAK,iBAAiB,GAAG;IAAE,iBAAiB,CAAC,EAAE,iBAAiB,CAAA;CAAE,CAAA;AAClE,KAAK,WAAW,GAAG;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAA;AACpD,KAAK,WAAW,GAAG;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAAA;AA+DnD;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,qBAAa,cAAc;IACvB;;;;OAIG;IACH,OAAO,CAAC,IAAI,CAAsB;IAElC;;;;OAIG;IACH,OAAO,CAAC,MAAM,CAAsB;IAEpC;;;;OAIG;IACH,OAAO,CAAC,IAAI,CAAS;IAErB;;;;OAIG;IACH,OAAO,CAAC,SAAS,CAAS;IAE1B;;;;OAIG;IACH,OAAO,CAAC,OAAO,CAAC,CAA2B;IAC3C,OAAO,CAAC,KAAK,CAAC,CAAQ;IAEtB;;;;;;;OAOG;gBACS,IAAI,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,CAAC,CAAC,KAAK,EAAE,MAAM,KAAK,MAAM,CAAC,EAAE,OAAO,CAAC,EAAE;QAAE,WAAW,CAAC,EAAE;YAAE,IAAI,EAAE,MAAM,CAAC;YAAC,EAAE,EAAE,MAAM,CAAA;SAAE,CAAC;QAAC,WAAW,CAAC,EAAE,OAAO,CAAA;KAAE;
|
|
1
|
+
{"version":3,"file":"useFenwickMapTree.d.ts","sourceRoot":"","sources":["../src/useFenwickMapTree.ts"],"names":[],"mappings":"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"}
|
package/dist/utils.d.cts
CHANGED
|
@@ -12,3 +12,18 @@
|
|
|
12
12
|
* @returns The clamped value. / クランプされた値。
|
|
13
13
|
*/
|
|
14
14
|
export declare const minmax: (value: number, min: number, max: number) => number;
|
|
15
|
+
/**
|
|
16
|
+
* Computes an element's CSS transform scale along one axis (visual px / layout px).
|
|
17
|
+
*
|
|
18
|
+
* 要素の指定軸方向 CSS transform スケール (視覚px / レイアウトpx) を算出。
|
|
19
|
+
* 祖先に transform: scale(...) があると client 座標 (視覚px) がレイアウトpx とズレるため、
|
|
20
|
+
* その比率でドラッグ量・クリック座標を補正する。分母には整数丸めされる offsetWidth/offsetHeight
|
|
21
|
+
* ではなく、transform の影響を受けず小数精度を持つ getComputedStyle のレイアウト寸法
|
|
22
|
+
* (content + padding + border) を優先して使い、transform なしなら厳密に 1 を返す。
|
|
23
|
+
* 取得不能時 (jsdom 等) は offset 寸法へフォールバックし、それも 0 なら 1 を返す。
|
|
24
|
+
*
|
|
25
|
+
* @param element The element to measure. / 計測対象要素。
|
|
26
|
+
* @param axis The axis to measure along ("x" = width, "y" = height). / 計測軸。
|
|
27
|
+
* @returns The visual/layout scale ratio (1 when unmeasurable). / 視覚/レイアウト比 (計測不能時 1)。
|
|
28
|
+
*/
|
|
29
|
+
export declare const getAxisScale: (element: HTMLElement, axis: "x" | "y") => number;
|
package/dist/utils.d.ts
CHANGED
|
@@ -12,4 +12,19 @@
|
|
|
12
12
|
* @returns The clamped value. / クランプされた値。
|
|
13
13
|
*/
|
|
14
14
|
export declare const minmax: (value: number, min: number, max: number) => number;
|
|
15
|
+
/**
|
|
16
|
+
* Computes an element's CSS transform scale along one axis (visual px / layout px).
|
|
17
|
+
*
|
|
18
|
+
* 要素の指定軸方向 CSS transform スケール (視覚px / レイアウトpx) を算出。
|
|
19
|
+
* 祖先に transform: scale(...) があると client 座標 (視覚px) がレイアウトpx とズレるため、
|
|
20
|
+
* その比率でドラッグ量・クリック座標を補正する。分母には整数丸めされる offsetWidth/offsetHeight
|
|
21
|
+
* ではなく、transform の影響を受けず小数精度を持つ getComputedStyle のレイアウト寸法
|
|
22
|
+
* (content + padding + border) を優先して使い、transform なしなら厳密に 1 を返す。
|
|
23
|
+
* 取得不能時 (jsdom 等) は offset 寸法へフォールバックし、それも 0 なら 1 を返す。
|
|
24
|
+
*
|
|
25
|
+
* @param element The element to measure. / 計測対象要素。
|
|
26
|
+
* @param axis The axis to measure along ("x" = width, "y" = height). / 計測軸。
|
|
27
|
+
* @returns The visual/layout scale ratio (1 when unmeasurable). / 視覚/レイアウト比 (計測不能時 1)。
|
|
28
|
+
*/
|
|
29
|
+
export declare const getAxisScale: (element: HTMLElement, axis: "x" | "y") => number;
|
|
15
30
|
//# sourceMappingURL=utils.d.ts.map
|
package/dist/utils.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../src/utils.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,MAAM,GAAI,OAAO,MAAM,EAAE,KAAK,MAAM,EAAE,KAAK,MAAM,KAAG,MAEhE,CAAA"}
|
|
1
|
+
{"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../src/utils.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,MAAM,GAAI,OAAO,MAAM,EAAE,KAAK,MAAM,EAAE,KAAK,MAAM,KAAG,MAEhE,CAAA;AAED;;;;;;;;;;;;;GAaG;AACH,eAAO,MAAM,YAAY,GAAI,SAAS,WAAW,EAAE,MAAM,GAAG,GAAG,GAAG,KAAG,MA0BpE,CAAA"}
|
package/package.json
CHANGED
package/src/ScrollBar.tsx
CHANGED
|
@@ -8,7 +8,7 @@ import { useCallback, useEffect, useLayoutEffect, useMemo, useRef, useState } fr
|
|
|
8
8
|
import { twMerge } from "tailwind-merge"
|
|
9
9
|
import { Logger } from "./logger.ts"
|
|
10
10
|
import { TapScrollCircle, type TapScrollCircleDragState, type TapScrollCircleHandle, type TapScrollCircleRenderProps } from "./TapScrollCircle.tsx"
|
|
11
|
-
import { minmax } from "./utils.ts"
|
|
11
|
+
import { getAxisScale, minmax } from "./utils.ts"
|
|
12
12
|
|
|
13
13
|
/**
|
|
14
14
|
* Configuration for scrollbar orientation (vertical or horizontal).
|
|
@@ -161,6 +161,14 @@ export type ScrollBarProps = {
|
|
|
161
161
|
enableArrowButtons?: boolean
|
|
162
162
|
/** Whether the scrollbar is horizontal. / スクロールバーが水平かどうか。 */
|
|
163
163
|
horizontal?: boolean
|
|
164
|
+
/**
|
|
165
|
+
* Opt-in: renders the tap-scroll circle on HORIZONTAL bars too (default false — the bare
|
|
166
|
+
* horizontal bar's shipped behavior is unchanged; VirtualGrid enables this for trillion-column
|
|
167
|
+
* reachability, v3.2.0). Vertical bars are unaffected by this flag.
|
|
168
|
+
* 横バーにもタップスクロールサークルを描画する opt-in (既定 false — 単体横バーの既定挙動は
|
|
169
|
+
* 不変。VirtualGrid が兆列到達性のために有効化する、v3.2.0)。縦バーには影響しない。
|
|
170
|
+
*/
|
|
171
|
+
enableHorizontalTapCircle?: boolean
|
|
164
172
|
/** The width of the scrollbar. / スクロールバーの幅。 */
|
|
165
173
|
scrollBarWidth?: number
|
|
166
174
|
/** Additional class names for the component. / コンポーネントの追加のクラス名。 */
|
|
@@ -582,6 +590,7 @@ export const ScrollBar = ({
|
|
|
582
590
|
enableTrackClick = true,
|
|
583
591
|
enableArrowButtons = true,
|
|
584
592
|
horizontal = false,
|
|
593
|
+
enableHorizontalTapCircle = false,
|
|
585
594
|
scrollBarWidth = 12,
|
|
586
595
|
className,
|
|
587
596
|
ariaControls,
|
|
@@ -650,8 +659,10 @@ export const ScrollBar = ({
|
|
|
650
659
|
const trackLength = Math.max(viewportSize - scrollBarWidth * 2, 0)
|
|
651
660
|
const rawThumbSize = scrollRatio * trackLength
|
|
652
661
|
const thumbSize = Math.min(Math.max(MIN_THUMB_SIZE, rawThumbSize || 0), trackLength || MIN_THUMB_SIZE)
|
|
653
|
-
// 最大スクロール位置
|
|
654
|
-
|
|
662
|
+
// 最大スクロール位置 (コンテンツが viewport に収まる場合は 0 へクランプ — 素の減算のままだと
|
|
663
|
+
// 負値が aria-valuemax へ露出して ARIA 違反 (valuemax < valuemin) になる。644 行の
|
|
664
|
+
// getScrollBarMetrics と同一のクランプ規約)
|
|
665
|
+
const maxScrollPosition = Math.max(contentSize - viewportSize, 0)
|
|
655
666
|
const effectiveTrackLength = Math.max(trackLength - thumbSize, 0)
|
|
656
667
|
// scrollPosition が範囲外 (コンテンツ縮小直後の未クランプ 1 フレームや iOS バウンス値) でも
|
|
657
668
|
// サムがトラック外へはみ出さないよう、レンダー時に [0, effectiveTrackLength] へクランプする。
|
|
@@ -1055,35 +1066,8 @@ export const ScrollBar = ({
|
|
|
1055
1066
|
* (content + padding + border) を優先して使い、transform なしなら厳密に 1 を返す。
|
|
1056
1067
|
* 取得不能時 (jsdom 等) は offset 寸法へフォールバックし、それも 0 なら 1 を返す。
|
|
1057
1068
|
*/
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
const rect = element.getBoundingClientRect()
|
|
1061
|
-
const visualSize = horizontal ? rect.width : rect.height
|
|
1062
|
-
// computed style の値 (px 文字列) を数値化する。解釈不能なら 0 とみなす。
|
|
1063
|
-
const parseSize = (value: string) => {
|
|
1064
|
-
const parsed = Number.parseFloat(value)
|
|
1065
|
-
return Number.isFinite(parsed) ? parsed : 0
|
|
1066
|
-
}
|
|
1067
|
-
const computed = window.getComputedStyle(element)
|
|
1068
|
-
const contentSize = parseSize(horizontal ? computed.width : computed.height)
|
|
1069
|
-
// rect (border-box) と同系になるよう content + padding + border を合成する。
|
|
1070
|
-
const computedLayoutSize =
|
|
1071
|
-
contentSize > 0
|
|
1072
|
-
? contentSize +
|
|
1073
|
-
parseSize(horizontal ? computed.paddingLeft : computed.paddingTop) +
|
|
1074
|
-
parseSize(horizontal ? computed.paddingRight : computed.paddingBottom) +
|
|
1075
|
-
parseSize(horizontal ? computed.borderLeftWidth : computed.borderTopWidth) +
|
|
1076
|
-
parseSize(horizontal ? computed.borderRightWidth : computed.borderBottomWidth)
|
|
1077
|
-
: 0
|
|
1078
|
-
// computed が取れない環境 (jsdom 等) では従来の offset 寸法へフォールバックする。
|
|
1079
|
-
const layoutSize = computedLayoutSize > 0 ? computedLayoutSize : horizontal ? element.offsetWidth : element.offsetHeight
|
|
1080
|
-
if (layoutSize <= 0 || visualSize <= 0) {
|
|
1081
|
-
return 1
|
|
1082
|
-
}
|
|
1083
|
-
return visualSize / layoutSize
|
|
1084
|
-
},
|
|
1085
|
-
[horizontal],
|
|
1086
|
-
)
|
|
1069
|
+
// 本体は共有ヘルパ getAxisScale (utils.ts — ScrollPane のポインタパンと同一 SSOT)
|
|
1070
|
+
const getMainAxisScale = useCallback((element: HTMLElement) => getAxisScale(element, horizontal ? "x" : "y"), [horizontal])
|
|
1087
1071
|
|
|
1088
1072
|
/**
|
|
1089
1073
|
* Pointer move handler for thumb dragging.
|
|
@@ -1504,9 +1488,10 @@ export const ScrollBar = ({
|
|
|
1504
1488
|
aria-valuemin={0}
|
|
1505
1489
|
aria-valuemax={maxScrollPosition}
|
|
1506
1490
|
aria-orientation={horizontal ? "horizontal" : "vertical"}>
|
|
1507
|
-
{!horizontal && scrollBarVisible && tapCircleEnabled && (
|
|
1491
|
+
{(!horizontal || enableHorizontalTapCircle) && scrollBarVisible && tapCircleEnabled && (
|
|
1508
1492
|
<TapScrollCircle
|
|
1509
1493
|
key="tap-circle"
|
|
1494
|
+
axis={horizontal ? "x" : "y"}
|
|
1510
1495
|
ref={tapCircleHandleRef}
|
|
1511
1496
|
className={twMerge("aqvs-scrollbar-tap-circle-wrapper", tapCircleClassName)}
|
|
1512
1497
|
size={tapCircleSize}
|
package/src/ScrollPane.tsx
CHANGED
|
@@ -2,7 +2,7 @@ import { forwardRef, useCallback, useEffect, useId, useImperativeHandle, useLayo
|
|
|
2
2
|
import { twMerge } from "tailwind-merge"
|
|
3
3
|
import { Logger } from "./logger.ts"
|
|
4
4
|
import { ScrollBar, type ScrollBarTapCircleOptions, type ScrollBarThumbOverlayRenderProps, TAP_SCROLL_CANCEL_EVENT } from "./ScrollBar.tsx"
|
|
5
|
-
import { minmax } from "./utils.ts"
|
|
5
|
+
import { getAxisScale, minmax } from "./utils.ts"
|
|
6
6
|
import { resolveWheelAxes } from "./wheelAxes.ts"
|
|
7
7
|
import { markWheelConsumed, wasWheelConsumed } from "./wheelConsumption.ts"
|
|
8
8
|
|
|
@@ -123,6 +123,19 @@ export type ScrollPaneProps = {
|
|
|
123
123
|
* 指定時は横スクロールを委譲処理し、未指定時でも Shift+ホイール 等の横ジェスチャは縦スクロールで横取りせず親要素のスクロールへ委ねる。
|
|
124
124
|
*/
|
|
125
125
|
onWheelHorizontal?: (deltaX: number) => void
|
|
126
|
+
/**
|
|
127
|
+
* Delegates the horizontal component of a pointer pan (touch / pen drag) upstream, in the
|
|
128
|
+
* SAME sign convention as onWheelHorizontal (positive = scroll right). Emitted as per-move
|
|
129
|
+
* increments, divided by the press-time ancestor scale (x axis). When set, drag activation
|
|
130
|
+
* also considers horizontal finger travel so a purely horizontal pan can start a drag.
|
|
131
|
+
* Vertical pan / inertia behavior is unchanged (the pane stays a 1-axis owner — this is a
|
|
132
|
+
* delegation seam, mirroring onWheelHorizontal; v3.2.0 VirtualGrid seam).
|
|
133
|
+
* ポインタパン (タッチ / ペン) の横成分を上流へ委譲する (符号は onWheelHorizontal と同一 —
|
|
134
|
+
* 正 = 右へスクロール)。per-move 増分で配送し、押下時採取の横軸祖先 scale で除算する。
|
|
135
|
+
* 指定時はドラッグ発動判定に横移動量も含める (純横パンでもドラッグが始まる)。縦のパン /
|
|
136
|
+
* 慣性は不変 — ペインは 1 軸所有者のままの委譲シーム (onWheelHorizontal の対、v3.2.0)。
|
|
137
|
+
*/
|
|
138
|
+
onPanHorizontal?: (deltaX: number) => void
|
|
126
139
|
/** Insets applied to the scrollable content area. / スクロール可能領域に適用するインセット。 */
|
|
127
140
|
contentInsets?: ScrollPaneContentInsets
|
|
128
141
|
/** The index of the first visible item. / 最初の可視アイテムのインデックス。 */
|
|
@@ -226,11 +239,19 @@ type DragState = {
|
|
|
226
239
|
pointerType: string
|
|
227
240
|
pointerId: number | null
|
|
228
241
|
startClientY: number
|
|
242
|
+
/** 横パン委譲用の押下時 clientX。onPanHorizontal 未指定時は使用されない。 */
|
|
243
|
+
startClientX: number
|
|
244
|
+
/** 横パン増分の前回 clientX (per-move 増分配送の基準)。 */
|
|
245
|
+
lastPanClientX: number
|
|
229
246
|
startScroll: number
|
|
230
247
|
isDragging: boolean
|
|
231
248
|
shouldCancelNextClick: boolean
|
|
232
249
|
clickResetTimer: number | null
|
|
233
250
|
velocitySamples: DragSample[]
|
|
251
|
+
/** press 時に採取した縦軸の視覚/レイアウト比 (祖先 transform scale — 補正の分母)。 */
|
|
252
|
+
scale: number
|
|
253
|
+
/** press 時に採取した横軸の視覚/レイアウト比 (onPanHorizontal 指定時のみ採取)。 */
|
|
254
|
+
scaleX: number
|
|
234
255
|
}
|
|
235
256
|
|
|
236
257
|
/**
|
|
@@ -304,6 +325,7 @@ export const ScrollPane = forwardRef<ScrollPaneHandle, ScrollPaneProps>(
|
|
|
304
325
|
wheelSpeedMultiplier = 1,
|
|
305
326
|
overscrollBehavior = "auto",
|
|
306
327
|
onWheelHorizontal,
|
|
328
|
+
onPanHorizontal,
|
|
307
329
|
contentInsets,
|
|
308
330
|
visibleStartIndex,
|
|
309
331
|
visibleEndIndex,
|
|
@@ -457,6 +479,10 @@ export const ScrollPane = forwardRef<ScrollPaneHandle, ScrollPaneProps>(
|
|
|
457
479
|
useEffect(() => {
|
|
458
480
|
onWheelHorizontalRef.current = onWheelHorizontal
|
|
459
481
|
}, [onWheelHorizontal])
|
|
482
|
+
const onPanHorizontalRef = useRef(onPanHorizontal)
|
|
483
|
+
useEffect(() => {
|
|
484
|
+
onPanHorizontalRef.current = onPanHorizontal
|
|
485
|
+
}, [onPanHorizontal])
|
|
460
486
|
// generation は慣性ループの世代トークン。stopInertia のたびに進み、
|
|
461
487
|
// step 実行中に同期的な停止指示が入った場合の再アーム (停止の上書き) を防ぐ。
|
|
462
488
|
const inertiaStateRef = useRef<{ frame: number | null; velocity: number; lastTimestamp: number | null; generation: number }>({
|
|
@@ -984,11 +1010,15 @@ export const ScrollPane = forwardRef<ScrollPaneHandle, ScrollPaneProps>(
|
|
|
984
1010
|
pointerType: "",
|
|
985
1011
|
pointerId: null,
|
|
986
1012
|
startClientY: 0,
|
|
1013
|
+
startClientX: 0,
|
|
1014
|
+
lastPanClientX: 0,
|
|
987
1015
|
startScroll: 0,
|
|
988
1016
|
isDragging: false,
|
|
989
1017
|
shouldCancelNextClick: false,
|
|
990
1018
|
clickResetTimer: null,
|
|
991
1019
|
velocitySamples: [],
|
|
1020
|
+
scale: 1,
|
|
1021
|
+
scaleX: 1,
|
|
992
1022
|
})
|
|
993
1023
|
|
|
994
1024
|
const enablePointerDragRef = useRef(enablePointerDrag)
|
|
@@ -1084,6 +1114,7 @@ export const ScrollPane = forwardRef<ScrollPaneHandle, ScrollPaneProps>(
|
|
|
1084
1114
|
state.isDragging = false
|
|
1085
1115
|
state.shouldCancelNextClick = shouldKeepClickSuppression
|
|
1086
1116
|
state.velocitySamples = []
|
|
1117
|
+
state.scale = 1
|
|
1087
1118
|
// 状態を確定させてから解放する (同期 lostpointercapture に引き継ぎを壊させない)
|
|
1088
1119
|
if (element?.hasPointerCapture(releasedPointerId)) {
|
|
1089
1120
|
element.releasePointerCapture(releasedPointerId)
|
|
@@ -1107,6 +1138,7 @@ export const ScrollPane = forwardRef<ScrollPaneHandle, ScrollPaneProps>(
|
|
|
1107
1138
|
state.startScroll = 0
|
|
1108
1139
|
state.isDragging = false
|
|
1109
1140
|
state.velocitySamples = []
|
|
1141
|
+
state.scale = 1
|
|
1110
1142
|
}
|
|
1111
1143
|
|
|
1112
1144
|
const clearClickTimer = () => {
|
|
@@ -1228,7 +1260,10 @@ export const ScrollPane = forwardRef<ScrollPaneHandle, ScrollPaneProps>(
|
|
|
1228
1260
|
}
|
|
1229
1261
|
if (!state.isDragging) {
|
|
1230
1262
|
const distanceY = Math.abs(event.clientY - state.startClientY)
|
|
1231
|
-
|
|
1263
|
+
// 横パン委譲時は横移動でも発動する (純横パンの取りこぼし防止 — 委譲なしの
|
|
1264
|
+
// 既存挙動は縦のみで不変)
|
|
1265
|
+
const distanceX = onPanHorizontalRef.current ? Math.abs(event.clientX - state.startClientX) : 0
|
|
1266
|
+
if (Math.max(distanceY, distanceX) < DRAG_ACTIVATION_THRESHOLD) {
|
|
1232
1267
|
return
|
|
1233
1268
|
}
|
|
1234
1269
|
startDragging(event)
|
|
@@ -1239,10 +1274,23 @@ export const ScrollPane = forwardRef<ScrollPaneHandle, ScrollPaneProps>(
|
|
|
1239
1274
|
|
|
1240
1275
|
pushVelocitySample(event.clientY)
|
|
1241
1276
|
|
|
1242
|
-
|
|
1277
|
+
// ÷scale: 視覚 px の指移動をレイアウト (論理) px へ (発動閾値 6px は指の物理
|
|
1278
|
+
// 移動量の判別なので視覚 px のまま — 意図的非対称)
|
|
1279
|
+
const deltaY = (event.clientY - state.startClientY) / (state.scale || 1)
|
|
1243
1280
|
const nextPosition = state.startScroll - deltaY
|
|
1244
1281
|
scrollToRef.current(nextPosition)
|
|
1245
1282
|
|
|
1283
|
+
// 横パン増分の委譲 (per-move 増分、符号は wheel と同一: 指が左 = コンテンツ左送り =
|
|
1284
|
+
// 右へスクロール = 正)。÷scaleX は縦 deltaY と同一式の横対称
|
|
1285
|
+
const panHorizontal = onPanHorizontalRef.current
|
|
1286
|
+
if (panHorizontal) {
|
|
1287
|
+
const rawDeltaX = event.clientX - state.lastPanClientX
|
|
1288
|
+
state.lastPanClientX = event.clientX
|
|
1289
|
+
if (rawDeltaX !== 0) {
|
|
1290
|
+
panHorizontal(-rawDeltaX / (state.scaleX || 1))
|
|
1291
|
+
}
|
|
1292
|
+
}
|
|
1293
|
+
|
|
1246
1294
|
if (event.cancelable) {
|
|
1247
1295
|
event.preventDefault()
|
|
1248
1296
|
}
|
|
@@ -1309,7 +1357,8 @@ export const ScrollPane = forwardRef<ScrollPaneHandle, ScrollPaneProps>(
|
|
|
1309
1357
|
if (lastSample && firstSample && lastSample.time !== firstSample.time) {
|
|
1310
1358
|
const deltaClientY = lastSample.clientY - firstSample.clientY
|
|
1311
1359
|
const deltaTime = lastSample.time - firstSample.time
|
|
1312
|
-
|
|
1360
|
+
// ÷scale: フリック速度も視覚 px/ms のままだと慣性走行距離が z 倍に歪む
|
|
1361
|
+
inertiaVelocity = -(deltaClientY / deltaTime) / (state.scale || 1)
|
|
1313
1362
|
}
|
|
1314
1363
|
}
|
|
1315
1364
|
|
|
@@ -1404,7 +1453,16 @@ export const ScrollPane = forwardRef<ScrollPaneHandle, ScrollPaneProps>(
|
|
|
1404
1453
|
state.pointerId = event.pointerId
|
|
1405
1454
|
state.pointerType = event.pointerType
|
|
1406
1455
|
state.startClientY = event.clientY
|
|
1456
|
+
state.startClientX = event.clientX
|
|
1457
|
+
state.lastPanClientX = event.clientX
|
|
1407
1458
|
state.startScroll = scrollPositionRef.current
|
|
1459
|
+
// 祖先 transform scale の押下時採取 (ScrollBar サム / トラックと同じ press キャッシュ
|
|
1460
|
+
// 規約): 視覚 px の指移動をレイアウト px へ換算しないと scale z 下でコンテンツが
|
|
1461
|
+
// 指速の z 倍で動く。move 毎の gBCR + getComputedStyle は 60-120Hz 熱経路の
|
|
1462
|
+
// 強制レイアウトになるため押下時 1 回に固定する
|
|
1463
|
+
state.scale = getAxisScale(element, "y")
|
|
1464
|
+
// 横パン委譲時のみ採取 (未指定の既存消費者に強制レイアウトを増やさない)
|
|
1465
|
+
state.scaleX = onPanHorizontalRef.current ? getAxisScale(element, "x") : 1
|
|
1408
1466
|
state.isDragging = false
|
|
1409
1467
|
state.shouldCancelNextClick = false
|
|
1410
1468
|
state.velocitySamples = []
|
package/src/TapScrollCircle.tsx
CHANGED
|
@@ -23,6 +23,12 @@ export type TapScrollCircleDragState = {
|
|
|
23
23
|
}
|
|
24
24
|
|
|
25
25
|
export type TapScrollCircleProps = {
|
|
26
|
+
/**
|
|
27
|
+
* Axis the direction/distance/dead-zone derivation reads (default "y" — vertical bars).
|
|
28
|
+
* Horizontal bars pass "x" so a horizontal circle drag scrolls horizontally (v3.2.0).
|
|
29
|
+
* 方向・距離・デッドゾーン導出が読む軸 (既定 "y" — 縦バー)。横バーは "x" を渡す (v3.2.0)。
|
|
30
|
+
*/
|
|
31
|
+
axis?: "x" | "y"
|
|
26
32
|
/** Called whenever the drag state updates. / ドラッグ状態が更新された際に呼び出されるコールバック。 */
|
|
27
33
|
onDragChange: (state: TapScrollCircleDragState) => void
|
|
28
34
|
/** Additional class names for styling. / 追加のスタイル用クラス名。 */
|
|
@@ -114,7 +120,7 @@ const defaultRenderVisual = ({ dragState, normalizedDistance }: TapScrollCircleR
|
|
|
114
120
|
* タップした方向へ伸縮するタッチ操作用サークル。
|
|
115
121
|
*/
|
|
116
122
|
export const TapScrollCircle = memo(
|
|
117
|
-
forwardRef<TapScrollCircleHandle, TapScrollCircleProps>(({ onDragChange, className, maxVisualDistance = 160, size = 40, style, opacity = 1, renderVisual }, ref) => {
|
|
123
|
+
forwardRef<TapScrollCircleHandle, TapScrollCircleProps>(({ onDragChange, className, maxVisualDistance = 160, size = 40, style, opacity = 1, renderVisual, axis = "y" }, ref) => {
|
|
118
124
|
const [dragState, setDragState] = useState<TapScrollCircleDragState>(INITIAL_STATE)
|
|
119
125
|
const pointerIdRef = useRef<number | null>(null)
|
|
120
126
|
// アクティブなドラッグ中に押下された別ポインタ (2 本目のタッチ) を「待機候補」として保持する。
|
|
@@ -144,8 +150,11 @@ export const TapScrollCircle = memo(
|
|
|
144
150
|
const { x, y } = centerRef.current
|
|
145
151
|
const offsetX = clientX - x
|
|
146
152
|
const offsetY = clientY - y
|
|
147
|
-
|
|
148
|
-
|
|
153
|
+
// 方向・距離・デッドゾーンの導出軸 (v3.2.0): 縦バーは offsetY、横バーは offsetX。
|
|
154
|
+
// 導出以外 (ヒステリシス・視覚) は軸非依存のまま
|
|
155
|
+
const primaryOffset = axis === "x" ? offsetX : offsetY
|
|
156
|
+
const distance = Math.abs(primaryOffset)
|
|
157
|
+
const rawDirection: -1 | 0 | 1 = distance < DEAD_ZONE ? 0 : primaryOffset < 0 ? -1 : 1
|
|
149
158
|
const previousDirection = lastStableDirectionRef.current
|
|
150
159
|
let direction: -1 | 0 | 1 = rawDirection
|
|
151
160
|
|
|
@@ -176,7 +185,7 @@ export const TapScrollCircle = memo(
|
|
|
176
185
|
pointerId: pointerIdRef.current,
|
|
177
186
|
})
|
|
178
187
|
},
|
|
179
|
-
[applyDragState],
|
|
188
|
+
[applyDragState, axis],
|
|
180
189
|
)
|
|
181
190
|
|
|
182
191
|
const releasePointerCapture = useCallback((pointerId: number | null) => {
|