first commit
This commit is contained in:
21
frontend/node_modules/compute-scroll-into-view/LICENSE
generated
vendored
Normal file
21
frontend/node_modules/compute-scroll-into-view/LICENSE
generated
vendored
Normal file
@@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2025 Cody Olsen
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
133
frontend/node_modules/compute-scroll-into-view/README.md
generated
vendored
Normal file
133
frontend/node_modules/compute-scroll-into-view/README.md
generated
vendored
Normal file
@@ -0,0 +1,133 @@
|
||||
[](https://npm-stat.com/charts.html?package=compute-scroll-into-view)
|
||||
[](https://www.npmjs.com/package/compute-scroll-into-view)
|
||||
[![gzip size][gzip-badge]][unpkg-dist]
|
||||
[![size][size-badge]][unpkg-dist]
|
||||
[](https://github.com/semantic-release/semantic-release)
|
||||
|
||||

|
||||
|
||||
Lower level API that is used by the [ponyfill](https://ponyfill.com) [scroll-into-view-if-needed](https://github.com/scroll-into-view/scroll-into-view-if-needed) to compute where (if needed) elements should scroll based on [options defined in the spec](https://developer.mozilla.org/en-US/docs/Web/API/Element/scrollIntoView) and the [`scrollMode: "if-needed"` draft spec proposal](https://github.com/w3c/csswg-drafts/pull/1805).
|
||||
Use this if you want the smallest possible bundlesize and is ok with implementing the actual scrolling yourself.
|
||||
|
||||
Scrolling SVG elements are supported, as well as Shadow DOM elements. The [VisualViewport](https://developer.mozilla.org/en-US/docs/Web/API/VisualViewport) API is also supported, ensuring scrolling works properly on modern devices. Quirksmode is also supported as long as you polyfill [`document.scrollingElement`](https://developer.mozilla.org/en-US/docs/Web/API/document/scrollingElement).
|
||||
|
||||
- [Install](#install)
|
||||
- [Usage](#usage)
|
||||
- [API](#api)
|
||||
- [compute(target, options)](#computetarget-options)
|
||||
- [options](#options)
|
||||
- [block](#block)
|
||||
- [inline](#inline)
|
||||
- [scrollMode](#scrollmode)
|
||||
- [boundary](#boundary)
|
||||
- [skipOverflowHiddenElements](#skipoverflowhiddenelements)
|
||||
|
||||
# Install
|
||||
|
||||
```bash
|
||||
npm i compute-scroll-into-view
|
||||
```
|
||||
|
||||
You can also use it from a CDN:
|
||||
|
||||
```js
|
||||
const { compute } = await import('https://esm.sh/compute-scroll-into-view')
|
||||
```
|
||||
|
||||
# Usage
|
||||
|
||||
```js
|
||||
import { compute } from 'compute-scroll-into-view'
|
||||
|
||||
const node = document.getElementById('hero')
|
||||
|
||||
// same behavior as Element.scrollIntoView({block: "nearest", inline: "nearest"})
|
||||
// see: https://developer.mozilla.org/en-US/docs/Web/API/Element/scrollIntoView
|
||||
const actions = compute(node, {
|
||||
scrollMode: 'if-needed',
|
||||
block: 'nearest',
|
||||
inline: 'nearest',
|
||||
})
|
||||
|
||||
// same behavior as Element.scrollIntoViewIfNeeded(true)
|
||||
// see: https://developer.mozilla.org/en-US/docs/Web/API/Element/scrollIntoViewIfNeeded
|
||||
const actions = compute(node, {
|
||||
scrollMode: 'if-needed',
|
||||
block: 'center',
|
||||
inline: 'center',
|
||||
})
|
||||
|
||||
// Then perform the scrolling, use scroll-into-view-if-needed if you don't want to implement this part
|
||||
actions.forEach(({ el, top, left }) => {
|
||||
el.scrollTop = top
|
||||
el.scrollLeft = left
|
||||
})
|
||||
```
|
||||
|
||||
# API
|
||||
|
||||
## compute(target, options)
|
||||
|
||||
## options
|
||||
|
||||
Type: `Object`
|
||||
|
||||
### [block](https://scroll-into-view.dev/#scroll-alignment)
|
||||
|
||||
Type: `'start' | 'center' | 'end' | 'nearest'`<br> Default: `'center'`
|
||||
|
||||
Control the logical scroll position on the y-axis. The spec states that the `block` direction is related to the [writing-mode](https://developer.mozilla.org/en-US/docs/Web/CSS/writing-mode), but this is not implemented yet in this library.
|
||||
This means that `block: 'start'` aligns to the top edge and `block: 'end'` to the bottom.
|
||||
|
||||
### [inline](https://scroll-into-view.dev/#scroll-alignment)
|
||||
|
||||
Type: `'start' | 'center' | 'end' | 'nearest'`<br> Default: `'nearest'`
|
||||
|
||||
Like `block` this is affected by the [writing-mode](https://developer.mozilla.org/en-US/docs/Web/CSS/writing-mode). In left-to-right pages `inline: 'start'` will align to the left edge. In right-to-left it should be flipped. This will be supported in a future release.
|
||||
|
||||
### [scrollMode](https://scroll-into-view.dev/#scrolling-if-needed)
|
||||
|
||||
Type: `'always' | 'if-needed'`<br> Default: `'always'`
|
||||
|
||||
This is a proposed addition to the spec that you can track here: https://github.com/w3c/csswg-drafts/pull/5677
|
||||
|
||||
This library will be updated to reflect any changes to the spec and will provide a migration path.
|
||||
To be backwards compatible with `Element.scrollIntoViewIfNeeded` if something is not 100% visible it will count as "needs scrolling". If you need a different visibility ratio your best option would be to implement an [Intersection Observer](https://developer.mozilla.org/en-US/docs/Web/API/Intersection_Observer_API).
|
||||
|
||||
### [boundary](https://scroll-into-view.dev/#limit-propagation)
|
||||
|
||||
Type: `Element | Function`
|
||||
|
||||
By default there is no boundary. All the parent elements of your target is checked until it reaches the viewport ([`document.scrollingElement`](https://developer.mozilla.org/en-US/docs/Web/API/document/scrollingElement)) when calculating layout and what to scroll.
|
||||
By passing a boundary you can short-circuit this loop depending on your needs:
|
||||
|
||||
- Prevent the browser window from scrolling.
|
||||
- Scroll elements into view in a list, without scrolling container elements.
|
||||
|
||||
You can also pass a function to do more dynamic checks to override the scroll scoping:
|
||||
|
||||
```js
|
||||
const actions = compute(target, {
|
||||
boundary: (parent) => {
|
||||
// By default `overflow: hidden` elements are allowed, only `overflow: visible | clip` is skipped as
|
||||
// this is required by the CSSOM spec
|
||||
if (getComputedStyle(parent)['overflow'] === 'hidden') {
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
},
|
||||
})
|
||||
```
|
||||
|
||||
### skipOverflowHiddenElements
|
||||
|
||||
Type: `Boolean`<br> Default: `false`
|
||||
|
||||
By default the [spec](https://drafts.csswg.org/cssom-view/#scrolling-box) states that `overflow: hidden` elements should be scrollable because it has [been used to allow programatic scrolling](https://drafts.csswg.org/css-overflow-3/#valdef-overflow-hidden). This behavior can sometimes lead to [scrolling issues](https://github.com/scroll-into-view/scroll-into-view-if-needed/pull/225#issue-186419520) when you have a node that is a child of an `overflow: hidden` node.
|
||||
|
||||
This package follows the convention [adopted by Firefox](https://hg.mozilla.org/integration/fx-team/rev/c48c3ec05012#l7.18) of setting a boolean option to _not_ scroll all nodes with `overflow: hidden` set.
|
||||
|
||||
[gzip-badge]: https://img.shields.io/bundlephobia/minzip/compute-scroll-into-view?label=gzip%20size&style=flat-square
|
||||
[size-badge]: https://img.shields.io/bundlephobia/min/compute-scroll-into-view?label=size&style=flat-square
|
||||
[unpkg-dist]: https://unpkg.com/compute-scroll-into-view/dist/
|
||||
1
frontend/node_modules/compute-scroll-into-view/dist/index.cjs
generated
vendored
Normal file
1
frontend/node_modules/compute-scroll-into-view/dist/index.cjs
generated
vendored
Normal file
@@ -0,0 +1 @@
|
||||
"use strict";Object.defineProperty(exports,"__esModule",{value:!0});const t=t=>"object"==typeof t&&null!=t&&1===t.nodeType,e=(t,e)=>(!e||"hidden"!==t)&&("visible"!==t&&"clip"!==t),o=(t,o)=>{if(t.clientHeight<t.scrollHeight||t.clientWidth<t.scrollWidth){const n=getComputedStyle(t,null);return e(n.overflowY,o)||e(n.overflowX,o)||(t=>{const e=(t=>{if(!t.ownerDocument||!t.ownerDocument.defaultView)return null;try{return t.ownerDocument.defaultView.frameElement}catch(t){return null}})(t);return!!e&&(e.clientHeight<t.scrollHeight||e.clientWidth<t.scrollWidth)})(t)}return!1},n=(t,e,o,n,l,r,i,s)=>r<t&&i>e||r>t&&i<e?0:r<=t&&s<=o||i>=e&&s>=o?r-t-n:i>e&&s<o||r<t&&s>o?i-e+l:0,l=t=>{const e=t.parentElement;return null==e?t.getRootNode().host||null:e};exports.compute=(e,r)=>{var i,s,d,c;if("undefined"==typeof document)return[];const{scrollMode:h,block:u,inline:f,boundary:a,skipOverflowHiddenElements:g}=r,p="function"==typeof a?a:t=>t!==a;if(!t(e))throw new TypeError("Invalid target");const m=document.scrollingElement||document.documentElement,w=[];let W=e;for(;t(W)&&p(W);){if(W=l(W),W===m){w.push(W);break}null!=W&&W===document.body&&o(W)&&!o(document.documentElement)||null!=W&&o(W,g)&&w.push(W)}const b=null!=(s=null==(i=window.visualViewport)?void 0:i.width)?s:innerWidth,H=null!=(c=null==(d=window.visualViewport)?void 0:d.height)?c:innerHeight,{scrollX:y,scrollY:M}=window,{height:v,width:E,top:x,right:C,bottom:I,left:R}=e.getBoundingClientRect(),{top:T,right:B,bottom:F,left:V}=(t=>{const e=window.getComputedStyle(t);return{top:parseFloat(e.scrollMarginTop)||0,right:parseFloat(e.scrollMarginRight)||0,bottom:parseFloat(e.scrollMarginBottom)||0,left:parseFloat(e.scrollMarginLeft)||0}})(e);let k="start"===u||"nearest"===u?x-T:"end"===u?I+F:x+v/2-T+F,D="center"===f?R+E/2-V+B:"end"===f?C+B:R-V;const L=[];for(let t=0;t<w.length;t++){const e=w[t],{height:l,width:r,top:i,right:s,bottom:d,left:c}=e.getBoundingClientRect();if("if-needed"===h&&x>=0&&R>=0&&I<=H&&C<=b&&(e===m&&!o(e)||x>=i&&I<=d&&R>=c&&C<=s))return L;const a=getComputedStyle(e),g=parseInt(a.borderLeftWidth,10),p=parseInt(a.borderTopWidth,10),W=parseInt(a.borderRightWidth,10),T=parseInt(a.borderBottomWidth,10);let B=0,F=0;const V="offsetWidth"in e?e.offsetWidth-e.clientWidth-g-W:0,S="offsetHeight"in e?e.offsetHeight-e.clientHeight-p-T:0,j="offsetWidth"in e?0===e.offsetWidth?0:r/e.offsetWidth:0,O="offsetHeight"in e?0===e.offsetHeight?0:l/e.offsetHeight:0;if(m===e)B="start"===u?k:"end"===u?k-H:"nearest"===u?n(M,M+H,H,p,T,M+k,M+k+v,v):k-H/2,F="start"===f?D:"center"===f?D-b/2:"end"===f?D-b:n(y,y+b,b,g,W,y+D,y+D+E,E),B=Math.max(0,B+M),F=Math.max(0,F+y);else{B="start"===u?k-i-p:"end"===u?k-d+T+S:"nearest"===u?n(i,d,l,p,T+S,k,k+v,v):k-(i+l/2)+S/2,F="start"===f?D-c-g:"center"===f?D-(c+r/2)+V/2:"end"===f?D-s+W+V:n(c,s,r,g,W+V,D,D+E,E);const{scrollLeft:t,scrollTop:o}=e;B=0===O?0:Math.max(0,Math.min(o+B/O,e.scrollHeight-l/O+S)),F=0===j?0:Math.max(0,Math.min(t+F/j,e.scrollWidth-r/j+V)),k+=o-B,D+=t-F}L.push({el:e,top:B,left:F})}return L};//# sourceMappingURL=index.cjs.map
|
||||
1
frontend/node_modules/compute-scroll-into-view/dist/index.cjs.map
generated
vendored
Normal file
1
frontend/node_modules/compute-scroll-into-view/dist/index.cjs.map
generated
vendored
Normal file
File diff suppressed because one or more lines are too long
75
frontend/node_modules/compute-scroll-into-view/dist/index.d.ts
generated
vendored
Normal file
75
frontend/node_modules/compute-scroll-into-view/dist/index.d.ts
generated
vendored
Normal file
@@ -0,0 +1,75 @@
|
||||
/** @public */
|
||||
export declare const compute: (
|
||||
target: Element,
|
||||
options: Options
|
||||
) => ScrollAction[]
|
||||
|
||||
/** @public */
|
||||
export declare interface Options {
|
||||
/**
|
||||
* Control the logical scroll position on the y-axis. The spec states that the `block` direction is related to the [writing-mode](https://developer.mozilla.org/en-US/docs/Web/CSS/writing-mode), but this is not implemented yet in this library.
|
||||
* This means that `block: 'start'` aligns to the top edge and `block: 'end'` to the bottom.
|
||||
* @defaultValue 'center'
|
||||
*/
|
||||
block?: ScrollLogicalPosition
|
||||
/**
|
||||
* Like `block` this is affected by the [writing-mode](https://developer.mozilla.org/en-US/docs/Web/CSS/writing-mode). In left-to-right pages `inline: 'start'` will align to the left edge. In right-to-left it should be flipped. This will be supported in a future release.
|
||||
* @defaultValue 'nearest'
|
||||
*/
|
||||
inline?: ScrollLogicalPosition
|
||||
/**
|
||||
* This is a proposed addition to the spec that you can track here: https://github.com/w3c/csswg-drafts/pull/5677
|
||||
*
|
||||
* This library will be updated to reflect any changes to the spec and will provide a migration path.
|
||||
* To be backwards compatible with `Element.scrollIntoViewIfNeeded` if something is not 100% visible it will count as "needs scrolling". If you need a different visibility ratio your best option would be to implement an [Intersection Observer](https://developer.mozilla.org/en-US/docs/Web/API/Intersection_Observer_API).
|
||||
* @defaultValue 'always'
|
||||
*/
|
||||
scrollMode?: ScrollMode
|
||||
/**
|
||||
* By default there is no boundary. All the parent elements of your target is checked until it reaches the viewport ([`document.scrollingElement`](https://developer.mozilla.org/en-US/docs/Web/API/document/scrollingElement)) when calculating layout and what to scroll.
|
||||
* By passing a boundary you can short-circuit this loop depending on your needs:
|
||||
*
|
||||
* - Prevent the browser window from scrolling.
|
||||
* - Scroll elements into view in a list, without scrolling container elements.
|
||||
*
|
||||
* You can also pass a function to do more dynamic checks to override the scroll scoping:
|
||||
*
|
||||
* ```js
|
||||
* let actions = compute(target, {
|
||||
* boundary: (parent) => {
|
||||
* // By default `overflow: hidden` elements are allowed, only `overflow: visible | clip` is skipped as
|
||||
* // this is required by the CSSOM spec
|
||||
* if (getComputedStyle(parent)['overflow'] === 'hidden') {
|
||||
* return false
|
||||
* }
|
||||
|
||||
* return true
|
||||
* },
|
||||
* })
|
||||
* ```
|
||||
* @defaultValue null
|
||||
*/
|
||||
boundary?: Element | ((parent: Element) => boolean) | null
|
||||
/**
|
||||
* New option that skips auto-scrolling all nodes with overflow: hidden set
|
||||
* See FF implementation: https://hg.mozilla.org/integration/fx-team/rev/c48c3ec05012#l7.18
|
||||
* @defaultValue false
|
||||
* @public
|
||||
*/
|
||||
skipOverflowHiddenElements?: boolean
|
||||
}
|
||||
|
||||
/** @public */
|
||||
export declare interface ScrollAction {
|
||||
el: Element
|
||||
top: number
|
||||
left: number
|
||||
}
|
||||
|
||||
/**
|
||||
* This new option is tracked in this PR, which is the most likely candidate at the time: https://github.com/w3c/csswg-drafts/pull/1805
|
||||
* @public
|
||||
*/
|
||||
export declare type ScrollMode = 'always' | 'if-needed'
|
||||
|
||||
export {}
|
||||
1
frontend/node_modules/compute-scroll-into-view/dist/index.js
generated
vendored
Normal file
1
frontend/node_modules/compute-scroll-into-view/dist/index.js
generated
vendored
Normal file
@@ -0,0 +1 @@
|
||||
const t=t=>"object"==typeof t&&null!=t&&1===t.nodeType,e=(t,e)=>(!e||"hidden"!==t)&&("visible"!==t&&"clip"!==t),n=(t,n)=>{if(t.clientHeight<t.scrollHeight||t.clientWidth<t.scrollWidth){const o=getComputedStyle(t,null);return e(o.overflowY,n)||e(o.overflowX,n)||(t=>{const e=(t=>{if(!t.ownerDocument||!t.ownerDocument.defaultView)return null;try{return t.ownerDocument.defaultView.frameElement}catch(t){return null}})(t);return!!e&&(e.clientHeight<t.scrollHeight||e.clientWidth<t.scrollWidth)})(t)}return!1},o=(t,e,n,o,l,r,i,s)=>r<t&&i>e||r>t&&i<e?0:r<=t&&s<=n||i>=e&&s>=n?r-t-o:i>e&&s<n||r<t&&s>n?i-e+l:0,l=t=>{const e=t.parentElement;return null==e?t.getRootNode().host||null:e},r=(e,r)=>{var i,s,d,h;if("undefined"==typeof document)return[];const{scrollMode:c,block:f,inline:u,boundary:a,skipOverflowHiddenElements:g}=r,p="function"==typeof a?a:t=>t!==a;if(!t(e))throw new TypeError("Invalid target");const m=document.scrollingElement||document.documentElement,w=[];let W=e;for(;t(W)&&p(W);){if(W=l(W),W===m){w.push(W);break}null!=W&&W===document.body&&n(W)&&!n(document.documentElement)||null!=W&&n(W,g)&&w.push(W)}const b=null!=(s=null==(i=window.visualViewport)?void 0:i.width)?s:innerWidth,H=null!=(h=null==(d=window.visualViewport)?void 0:d.height)?h:innerHeight,{scrollX:y,scrollY:M}=window,{height:v,width:E,top:x,right:C,bottom:I,left:R}=e.getBoundingClientRect(),{top:T,right:B,bottom:F,left:V}=(t=>{const e=window.getComputedStyle(t);return{top:parseFloat(e.scrollMarginTop)||0,right:parseFloat(e.scrollMarginRight)||0,bottom:parseFloat(e.scrollMarginBottom)||0,left:parseFloat(e.scrollMarginLeft)||0}})(e);let k="start"===f||"nearest"===f?x-T:"end"===f?I+F:x+v/2-T+F,D="center"===u?R+E/2-V+B:"end"===u?C+B:R-V;const L=[];for(let t=0;t<w.length;t++){const e=w[t],{height:l,width:r,top:i,right:s,bottom:d,left:h}=e.getBoundingClientRect();if("if-needed"===c&&x>=0&&R>=0&&I<=H&&C<=b&&(e===m&&!n(e)||x>=i&&I<=d&&R>=h&&C<=s))return L;const a=getComputedStyle(e),g=parseInt(a.borderLeftWidth,10),p=parseInt(a.borderTopWidth,10),W=parseInt(a.borderRightWidth,10),T=parseInt(a.borderBottomWidth,10);let B=0,F=0;const V="offsetWidth"in e?e.offsetWidth-e.clientWidth-g-W:0,S="offsetHeight"in e?e.offsetHeight-e.clientHeight-p-T:0,X="offsetWidth"in e?0===e.offsetWidth?0:r/e.offsetWidth:0,Y="offsetHeight"in e?0===e.offsetHeight?0:l/e.offsetHeight:0;if(m===e)B="start"===f?k:"end"===f?k-H:"nearest"===f?o(M,M+H,H,p,T,M+k,M+k+v,v):k-H/2,F="start"===u?D:"center"===u?D-b/2:"end"===u?D-b:o(y,y+b,b,g,W,y+D,y+D+E,E),B=Math.max(0,B+M),F=Math.max(0,F+y);else{B="start"===f?k-i-p:"end"===f?k-d+T+S:"nearest"===f?o(i,d,l,p,T+S,k,k+v,v):k-(i+l/2)+S/2,F="start"===u?D-h-g:"center"===u?D-(h+r/2)+V/2:"end"===u?D-s+W+V:o(h,s,r,g,W+V,D,D+E,E);const{scrollLeft:t,scrollTop:n}=e;B=0===Y?0:Math.max(0,Math.min(n+B/Y,e.scrollHeight-l/Y+S)),F=0===X?0:Math.max(0,Math.min(t+F/X,e.scrollWidth-r/X+V)),k+=n-B,D+=t-F}L.push({el:e,top:B,left:F})}return L};export{r as compute};//# sourceMappingURL=index.js.map
|
||||
1
frontend/node_modules/compute-scroll-into-view/dist/index.js.map
generated
vendored
Normal file
1
frontend/node_modules/compute-scroll-into-view/dist/index.js.map
generated
vendored
Normal file
File diff suppressed because one or more lines are too long
82
frontend/node_modules/compute-scroll-into-view/package.json
generated
vendored
Normal file
82
frontend/node_modules/compute-scroll-into-view/package.json
generated
vendored
Normal file
@@ -0,0 +1,82 @@
|
||||
{
|
||||
"name": "compute-scroll-into-view",
|
||||
"version": "3.1.1",
|
||||
"description": "The engine that powers scroll-into-view-if-needed",
|
||||
"keywords": [
|
||||
"if-needed",
|
||||
"scroll",
|
||||
"scroll-into-view",
|
||||
"scroll-into-view-if-needed",
|
||||
"scrollIntoView",
|
||||
"scrollIntoViewIfNeeded",
|
||||
"scrollMode",
|
||||
"typescript"
|
||||
],
|
||||
"homepage": "https://scroll-into-view.dev",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/scroll-into-view/compute-scroll-into-view.git"
|
||||
},
|
||||
"license": "MIT",
|
||||
"author": "Cody Olsen",
|
||||
"sideEffects": false,
|
||||
"type": "module",
|
||||
"exports": {
|
||||
".": {
|
||||
"types": "./dist/index.d.ts",
|
||||
"source": "./src/index.ts",
|
||||
"require": "./dist/index.cjs",
|
||||
"import": "./dist/index.js",
|
||||
"default": "./dist/index.js"
|
||||
},
|
||||
"./package.json": "./package.json"
|
||||
},
|
||||
"main": "./dist/index.cjs",
|
||||
"module": "./dist/index.js",
|
||||
"source": "./src/index.ts",
|
||||
"typings": "./dist/index.d.ts",
|
||||
"files": [
|
||||
"dist",
|
||||
"src"
|
||||
],
|
||||
"scripts": {
|
||||
"prebuild": "npx rimraf 'dist'",
|
||||
"build": "pkg build --strict",
|
||||
"prepublishOnly": "npm run build",
|
||||
"test": "npx cross-env JEST_PUPPETEER_CONFIG='jest-puppeteer.config.cjs' jest -c integration/jest.config.cjs",
|
||||
"typecheck": "tsc"
|
||||
},
|
||||
"browserslist": [
|
||||
"> 0.2% and supports es6-module and supports es6-module-dynamic-import and not dead",
|
||||
"maintained node versions"
|
||||
],
|
||||
"prettier": {
|
||||
"semi": false,
|
||||
"singleQuote": true
|
||||
},
|
||||
"devDependencies": {
|
||||
"@sanity/pkg-utils": "^2.2.5",
|
||||
"@sanity/semantic-release-preset": "^4.0.0",
|
||||
"@types/expect-puppeteer": "^5.0.2",
|
||||
"@types/jest": "^29.4.0",
|
||||
"@types/jest-environment-puppeteer": "^5.0.3",
|
||||
"@types/puppeteer": "^7.0.4",
|
||||
"cross-env": "^7.0.3",
|
||||
"jest": "^29.5.0",
|
||||
"jest-junit": "^15.0.0",
|
||||
"jest-puppeteer": "^8.0.0",
|
||||
"prettier": "^2.8.4",
|
||||
"prettier-plugin-packagejson": "^2.4.3",
|
||||
"puppeteer": "^19.7.0",
|
||||
"rimraf": "^4.1.2",
|
||||
"serve": "^14.2.0",
|
||||
"typescript": "^5.0.0"
|
||||
},
|
||||
"bundlesize": [
|
||||
{
|
||||
"path": "./dist/index.js",
|
||||
"maxSize": "3 kB",
|
||||
"compression": "none"
|
||||
}
|
||||
]
|
||||
}
|
||||
566
frontend/node_modules/compute-scroll-into-view/src/index.ts
generated
vendored
Normal file
566
frontend/node_modules/compute-scroll-into-view/src/index.ts
generated
vendored
Normal file
@@ -0,0 +1,566 @@
|
||||
// Compute what scrolling needs to be done on required scrolling boxes for target to be in view
|
||||
|
||||
// The type names here are named after the spec to make it easier to find more information around what they mean:
|
||||
// To reduce churn and reduce things that need be maintained things from the official TS DOM library is used here
|
||||
// https://drafts.csswg.org/cssom-view/
|
||||
|
||||
// For a definition on what is "block flow direction" exactly, check this: https://drafts.csswg.org/css-writing-modes-4/#block-flow-direction
|
||||
|
||||
/**
|
||||
* This new option is tracked in this PR, which is the most likely candidate at the time: https://github.com/w3c/csswg-drafts/pull/1805
|
||||
* @public
|
||||
*/
|
||||
export type ScrollMode = 'always' | 'if-needed'
|
||||
|
||||
/** @public */
|
||||
export interface Options {
|
||||
/**
|
||||
* Control the logical scroll position on the y-axis. The spec states that the `block` direction is related to the [writing-mode](https://developer.mozilla.org/en-US/docs/Web/CSS/writing-mode), but this is not implemented yet in this library.
|
||||
* This means that `block: 'start'` aligns to the top edge and `block: 'end'` to the bottom.
|
||||
* @defaultValue 'center'
|
||||
*/
|
||||
block?: ScrollLogicalPosition
|
||||
/**
|
||||
* Like `block` this is affected by the [writing-mode](https://developer.mozilla.org/en-US/docs/Web/CSS/writing-mode). In left-to-right pages `inline: 'start'` will align to the left edge. In right-to-left it should be flipped. This will be supported in a future release.
|
||||
* @defaultValue 'nearest'
|
||||
*/
|
||||
inline?: ScrollLogicalPosition
|
||||
/**
|
||||
* This is a proposed addition to the spec that you can track here: https://github.com/w3c/csswg-drafts/pull/5677
|
||||
*
|
||||
* This library will be updated to reflect any changes to the spec and will provide a migration path.
|
||||
* To be backwards compatible with `Element.scrollIntoViewIfNeeded` if something is not 100% visible it will count as "needs scrolling". If you need a different visibility ratio your best option would be to implement an [Intersection Observer](https://developer.mozilla.org/en-US/docs/Web/API/Intersection_Observer_API).
|
||||
* @defaultValue 'always'
|
||||
*/
|
||||
scrollMode?: ScrollMode
|
||||
/**
|
||||
* By default there is no boundary. All the parent elements of your target is checked until it reaches the viewport ([`document.scrollingElement`](https://developer.mozilla.org/en-US/docs/Web/API/document/scrollingElement)) when calculating layout and what to scroll.
|
||||
* By passing a boundary you can short-circuit this loop depending on your needs:
|
||||
*
|
||||
* - Prevent the browser window from scrolling.
|
||||
* - Scroll elements into view in a list, without scrolling container elements.
|
||||
*
|
||||
* You can also pass a function to do more dynamic checks to override the scroll scoping:
|
||||
*
|
||||
* ```js
|
||||
* let actions = compute(target, {
|
||||
* boundary: (parent) => {
|
||||
* // By default `overflow: hidden` elements are allowed, only `overflow: visible | clip` is skipped as
|
||||
* // this is required by the CSSOM spec
|
||||
* if (getComputedStyle(parent)['overflow'] === 'hidden') {
|
||||
* return false
|
||||
* }
|
||||
|
||||
* return true
|
||||
* },
|
||||
* })
|
||||
* ```
|
||||
* @defaultValue null
|
||||
*/
|
||||
boundary?: Element | ((parent: Element) => boolean) | null
|
||||
/**
|
||||
* New option that skips auto-scrolling all nodes with overflow: hidden set
|
||||
* See FF implementation: https://hg.mozilla.org/integration/fx-team/rev/c48c3ec05012#l7.18
|
||||
* @defaultValue false
|
||||
* @public
|
||||
*/
|
||||
skipOverflowHiddenElements?: boolean
|
||||
}
|
||||
|
||||
/** @public */
|
||||
export interface ScrollAction {
|
||||
el: Element
|
||||
top: number
|
||||
left: number
|
||||
}
|
||||
|
||||
// @TODO better shadowdom test, 11 = document fragment
|
||||
const isElement = (el: any): el is Element =>
|
||||
typeof el === 'object' && el != null && el.nodeType === 1
|
||||
|
||||
const canOverflow = (
|
||||
overflow: string | null,
|
||||
skipOverflowHiddenElements?: boolean
|
||||
) => {
|
||||
if (skipOverflowHiddenElements && overflow === 'hidden') {
|
||||
return false
|
||||
}
|
||||
|
||||
return overflow !== 'visible' && overflow !== 'clip'
|
||||
}
|
||||
|
||||
const getFrameElement = (el: Element) => {
|
||||
if (!el.ownerDocument || !el.ownerDocument.defaultView) {
|
||||
return null
|
||||
}
|
||||
|
||||
try {
|
||||
return el.ownerDocument.defaultView.frameElement
|
||||
} catch (e) {
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
const isHiddenByFrame = (el: Element): boolean => {
|
||||
const frame = getFrameElement(el)
|
||||
if (!frame) {
|
||||
return false
|
||||
}
|
||||
|
||||
return (
|
||||
frame.clientHeight < el.scrollHeight || frame.clientWidth < el.scrollWidth
|
||||
)
|
||||
}
|
||||
|
||||
const isScrollable = (el: Element, skipOverflowHiddenElements?: boolean) => {
|
||||
if (el.clientHeight < el.scrollHeight || el.clientWidth < el.scrollWidth) {
|
||||
const style = getComputedStyle(el, null)
|
||||
return (
|
||||
canOverflow(style.overflowY, skipOverflowHiddenElements) ||
|
||||
canOverflow(style.overflowX, skipOverflowHiddenElements) ||
|
||||
isHiddenByFrame(el)
|
||||
)
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
/**
|
||||
* Find out which edge to align against when logical scroll position is "nearest"
|
||||
* Interesting fact: "nearest" works similarily to "if-needed", if the element is fully visible it will not scroll it
|
||||
*
|
||||
* Legends:
|
||||
* ┌────────┐ ┏ ━ ━ ━ ┓
|
||||
* │ target │ frame
|
||||
* └────────┘ ┗ ━ ━ ━ ┛
|
||||
*/
|
||||
const alignNearest = (
|
||||
scrollingEdgeStart: number,
|
||||
scrollingEdgeEnd: number,
|
||||
scrollingSize: number,
|
||||
scrollingBorderStart: number,
|
||||
scrollingBorderEnd: number,
|
||||
elementEdgeStart: number,
|
||||
elementEdgeEnd: number,
|
||||
elementSize: number
|
||||
) => {
|
||||
/**
|
||||
* If element edge A and element edge B are both outside scrolling box edge A and scrolling box edge B
|
||||
*
|
||||
* ┌──┐
|
||||
* ┏━│━━│━┓
|
||||
* │ │
|
||||
* ┃ │ │ ┃ do nothing
|
||||
* │ │
|
||||
* ┗━│━━│━┛
|
||||
* └──┘
|
||||
*
|
||||
* If element edge C and element edge D are both outside scrolling box edge C and scrolling box edge D
|
||||
*
|
||||
* ┏ ━ ━ ━ ━ ┓
|
||||
* ┌───────────┐
|
||||
* │┃ ┃│ do nothing
|
||||
* └───────────┘
|
||||
* ┗ ━ ━ ━ ━ ┛
|
||||
*/
|
||||
if (
|
||||
(elementEdgeStart < scrollingEdgeStart &&
|
||||
elementEdgeEnd > scrollingEdgeEnd) ||
|
||||
(elementEdgeStart > scrollingEdgeStart && elementEdgeEnd < scrollingEdgeEnd)
|
||||
) {
|
||||
return 0
|
||||
}
|
||||
|
||||
/**
|
||||
* If element edge A is outside scrolling box edge A and element height is less than scrolling box height
|
||||
*
|
||||
* ┌──┐
|
||||
* ┏━│━━│━┓ ┏━┌━━┐━┓
|
||||
* └──┘ │ │
|
||||
* from ┃ ┃ to ┃ └──┘ ┃
|
||||
*
|
||||
* ┗━ ━━ ━┛ ┗━ ━━ ━┛
|
||||
*
|
||||
* If element edge B is outside scrolling box edge B and element height is greater than scrolling box height
|
||||
*
|
||||
* ┏━ ━━ ━┓ ┏━┌━━┐━┓
|
||||
* │ │
|
||||
* from ┃ ┌──┐ ┃ to ┃ │ │ ┃
|
||||
* │ │ │ │
|
||||
* ┗━│━━│━┛ ┗━│━━│━┛
|
||||
* │ │ └──┘
|
||||
* │ │
|
||||
* └──┘
|
||||
*
|
||||
* If element edge C is outside scrolling box edge C and element width is less than scrolling box width
|
||||
*
|
||||
* from to
|
||||
* ┏ ━ ━ ━ ━ ┓ ┏ ━ ━ ━ ━ ┓
|
||||
* ┌───┐ ┌───┐
|
||||
* │ ┃ │ ┃ ┃ │ ┃
|
||||
* └───┘ └───┘
|
||||
* ┗ ━ ━ ━ ━ ┛ ┗ ━ ━ ━ ━ ┛
|
||||
*
|
||||
* If element edge D is outside scrolling box edge D and element width is greater than scrolling box width
|
||||
*
|
||||
* from to
|
||||
* ┏ ━ ━ ━ ━ ┓ ┏ ━ ━ ━ ━ ┓
|
||||
* ┌───────────┐ ┌───────────┐
|
||||
* ┃ │ ┃ │ ┃ ┃ │
|
||||
* └───────────┘ └───────────┘
|
||||
* ┗ ━ ━ ━ ━ ┛ ┗ ━ ━ ━ ━ ┛
|
||||
*/
|
||||
if (
|
||||
(elementEdgeStart <= scrollingEdgeStart && elementSize <= scrollingSize) ||
|
||||
(elementEdgeEnd >= scrollingEdgeEnd && elementSize >= scrollingSize)
|
||||
) {
|
||||
return elementEdgeStart - scrollingEdgeStart - scrollingBorderStart
|
||||
}
|
||||
|
||||
/**
|
||||
* If element edge B is outside scrolling box edge B and element height is less than scrolling box height
|
||||
*
|
||||
* ┏━ ━━ ━┓ ┏━ ━━ ━┓
|
||||
*
|
||||
* from ┃ ┃ to ┃ ┌──┐ ┃
|
||||
* ┌──┐ │ │
|
||||
* ┗━│━━│━┛ ┗━└━━┘━┛
|
||||
* └──┘
|
||||
*
|
||||
* If element edge A is outside scrolling box edge A and element height is greater than scrolling box height
|
||||
*
|
||||
* ┌──┐
|
||||
* │ │
|
||||
* │ │ ┌──┐
|
||||
* ┏━│━━│━┓ ┏━│━━│━┓
|
||||
* │ │ │ │
|
||||
* from ┃ └──┘ ┃ to ┃ │ │ ┃
|
||||
* │ │
|
||||
* ┗━ ━━ ━┛ ┗━└━━┘━┛
|
||||
*
|
||||
* If element edge C is outside scrolling box edge C and element width is greater than scrolling box width
|
||||
*
|
||||
* from to
|
||||
* ┏ ━ ━ ━ ━ ┓ ┏ ━ ━ ━ ━ ┓
|
||||
* ┌───────────┐ ┌───────────┐
|
||||
* │ ┃ │ ┃ │ ┃ ┃
|
||||
* └───────────┘ └───────────┘
|
||||
* ┗ ━ ━ ━ ━ ┛ ┗ ━ ━ ━ ━ ┛
|
||||
*
|
||||
* If element edge D is outside scrolling box edge D and element width is less than scrolling box width
|
||||
*
|
||||
* from to
|
||||
* ┏ ━ ━ ━ ━ ┓ ┏ ━ ━ ━ ━ ┓
|
||||
* ┌───┐ ┌───┐
|
||||
* ┃ │ ┃ │ ┃ │ ┃
|
||||
* └───┘ └───┘
|
||||
* ┗ ━ ━ ━ ━ ┛ ┗ ━ ━ ━ ━ ┛
|
||||
*
|
||||
*/
|
||||
if (
|
||||
(elementEdgeEnd > scrollingEdgeEnd && elementSize < scrollingSize) ||
|
||||
(elementEdgeStart < scrollingEdgeStart && elementSize > scrollingSize)
|
||||
) {
|
||||
return elementEdgeEnd - scrollingEdgeEnd + scrollingBorderEnd
|
||||
}
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
const getParentElement = (element: Node): Element | null => {
|
||||
const parent = element.parentElement
|
||||
if (parent == null) {
|
||||
return (element.getRootNode() as ShadowRoot).host || null
|
||||
}
|
||||
return parent
|
||||
}
|
||||
|
||||
const getScrollMargins = (target: Element) => {
|
||||
const computedStyle = window.getComputedStyle(target)
|
||||
return {
|
||||
top: parseFloat(computedStyle.scrollMarginTop) || 0,
|
||||
right: parseFloat(computedStyle.scrollMarginRight) || 0,
|
||||
bottom: parseFloat(computedStyle.scrollMarginBottom) || 0,
|
||||
left: parseFloat(computedStyle.scrollMarginLeft) || 0,
|
||||
}
|
||||
}
|
||||
|
||||
/** @public */
|
||||
export const compute = (target: Element, options: Options): ScrollAction[] => {
|
||||
if (typeof document === 'undefined') {
|
||||
// If there's no DOM we assume it's not in a browser environment
|
||||
return []
|
||||
}
|
||||
|
||||
const { scrollMode, block, inline, boundary, skipOverflowHiddenElements } =
|
||||
options
|
||||
// Allow using a callback to check the boundary
|
||||
// The default behavior is to check if the current target matches the boundary element or not
|
||||
// If undefined it'll check that target is never undefined (can happen as we recurse up the tree)
|
||||
const checkBoundary =
|
||||
typeof boundary === 'function' ? boundary : (node: any) => node !== boundary
|
||||
|
||||
if (!isElement(target)) {
|
||||
throw new TypeError('Invalid target')
|
||||
}
|
||||
|
||||
// Used to handle the top most element that can be scrolled
|
||||
const scrollingElement = document.scrollingElement || document.documentElement
|
||||
|
||||
// Collect all the scrolling boxes, as defined in the spec: https://drafts.csswg.org/cssom-view/#scrolling-box
|
||||
const frames: Element[] = []
|
||||
let cursor: Element | null = target
|
||||
while (isElement(cursor) && checkBoundary(cursor)) {
|
||||
// Move cursor to parent
|
||||
cursor = getParentElement(cursor)
|
||||
|
||||
// Stop when we reach the viewport
|
||||
if (cursor === scrollingElement) {
|
||||
frames.push(cursor)
|
||||
break
|
||||
}
|
||||
|
||||
// Skip document.body if it's not the scrollingElement and documentElement isn't independently scrollable
|
||||
if (
|
||||
cursor != null &&
|
||||
cursor === document.body &&
|
||||
isScrollable(cursor) &&
|
||||
!isScrollable(document.documentElement)
|
||||
) {
|
||||
continue
|
||||
}
|
||||
|
||||
// Now we check if the element is scrollable, this code only runs if the loop haven't already hit the viewport or a custom boundary
|
||||
if (cursor != null && isScrollable(cursor, skipOverflowHiddenElements)) {
|
||||
frames.push(cursor)
|
||||
}
|
||||
}
|
||||
|
||||
// Support pinch-zooming properly, making sure elements scroll into the visual viewport
|
||||
// Browsers that don't support visualViewport will report the layout viewport dimensions on document.documentElement.clientWidth/Height
|
||||
// and viewport dimensions on window.innerWidth/Height
|
||||
// https://www.quirksmode.org/mobile/viewports2.html
|
||||
// https://bokand.github.io/viewport/index.html
|
||||
const viewportWidth = window.visualViewport?.width ?? innerWidth
|
||||
const viewportHeight = window.visualViewport?.height ?? innerHeight
|
||||
const { scrollX, scrollY } = window
|
||||
|
||||
const {
|
||||
height: targetHeight,
|
||||
width: targetWidth,
|
||||
top: targetTop,
|
||||
right: targetRight,
|
||||
bottom: targetBottom,
|
||||
left: targetLeft,
|
||||
} = target.getBoundingClientRect()
|
||||
const {
|
||||
top: marginTop,
|
||||
right: marginRight,
|
||||
bottom: marginBottom,
|
||||
left: marginLeft,
|
||||
} = getScrollMargins(target)
|
||||
|
||||
// These values mutate as we loop through and generate scroll coordinates
|
||||
let targetBlock: number =
|
||||
block === 'start' || block === 'nearest'
|
||||
? targetTop - marginTop
|
||||
: block === 'end'
|
||||
? targetBottom + marginBottom
|
||||
: targetTop + targetHeight / 2 - marginTop + marginBottom // block === 'center
|
||||
let targetInline: number =
|
||||
inline === 'center'
|
||||
? targetLeft + targetWidth / 2 - marginLeft + marginRight
|
||||
: inline === 'end'
|
||||
? targetRight + marginRight
|
||||
: targetLeft - marginLeft // inline === 'start || inline === 'nearest
|
||||
|
||||
// Collect new scroll positions
|
||||
const computations: ScrollAction[] = []
|
||||
// In chrome there's no longer a difference between caching the `frames.length` to a var or not, so we don't in this case (size > speed anyways)
|
||||
for (let index = 0; index < frames.length; index++) {
|
||||
const frame = frames[index]
|
||||
|
||||
// @TODO add a shouldScroll hook here that allows userland code to take control
|
||||
|
||||
const { height, width, top, right, bottom, left } =
|
||||
frame.getBoundingClientRect()
|
||||
|
||||
// If the element is already visible we can end it here
|
||||
// @TODO targetBlock and targetInline should be taken into account to be compliant with https://github.com/w3c/csswg-drafts/pull/1805/files#diff-3c17f0e43c20f8ecf89419d49e7ef5e0R1333
|
||||
if (
|
||||
scrollMode === 'if-needed' &&
|
||||
targetTop >= 0 &&
|
||||
targetLeft >= 0 &&
|
||||
targetBottom <= viewportHeight &&
|
||||
targetRight <= viewportWidth &&
|
||||
// scrollingElement is added to the frames array even if it's not scrollable, in which case checking its bounds is not required
|
||||
((frame === scrollingElement && !isScrollable(frame)) ||
|
||||
(targetTop >= top &&
|
||||
targetBottom <= bottom &&
|
||||
targetLeft >= left &&
|
||||
targetRight <= right))
|
||||
) {
|
||||
// Break the loop and return the computations for things that are not fully visible
|
||||
return computations
|
||||
}
|
||||
|
||||
const frameStyle = getComputedStyle(frame)
|
||||
const borderLeft = parseInt(frameStyle.borderLeftWidth as string, 10)
|
||||
const borderTop = parseInt(frameStyle.borderTopWidth as string, 10)
|
||||
const borderRight = parseInt(frameStyle.borderRightWidth as string, 10)
|
||||
const borderBottom = parseInt(frameStyle.borderBottomWidth as string, 10)
|
||||
|
||||
let blockScroll: number = 0
|
||||
let inlineScroll: number = 0
|
||||
|
||||
// The property existance checks for offfset[Width|Height] is because only HTMLElement objects have them, but any Element might pass by here
|
||||
// @TODO find out if the "as HTMLElement" overrides can be dropped
|
||||
const scrollbarWidth =
|
||||
'offsetWidth' in frame
|
||||
? (frame as HTMLElement).offsetWidth -
|
||||
(frame as HTMLElement).clientWidth -
|
||||
borderLeft -
|
||||
borderRight
|
||||
: 0
|
||||
const scrollbarHeight =
|
||||
'offsetHeight' in frame
|
||||
? (frame as HTMLElement).offsetHeight -
|
||||
(frame as HTMLElement).clientHeight -
|
||||
borderTop -
|
||||
borderBottom
|
||||
: 0
|
||||
|
||||
const scaleX =
|
||||
'offsetWidth' in frame
|
||||
? (frame as HTMLElement).offsetWidth === 0
|
||||
? 0
|
||||
: width / (frame as HTMLElement).offsetWidth
|
||||
: 0
|
||||
const scaleY =
|
||||
'offsetHeight' in frame
|
||||
? (frame as HTMLElement).offsetHeight === 0
|
||||
? 0
|
||||
: height / (frame as HTMLElement).offsetHeight
|
||||
: 0
|
||||
|
||||
if (scrollingElement === frame) {
|
||||
// Handle viewport logic (document.documentElement or document.body)
|
||||
|
||||
if (block === 'start') {
|
||||
blockScroll = targetBlock
|
||||
} else if (block === 'end') {
|
||||
blockScroll = targetBlock - viewportHeight
|
||||
} else if (block === 'nearest') {
|
||||
blockScroll = alignNearest(
|
||||
scrollY,
|
||||
scrollY + viewportHeight,
|
||||
viewportHeight,
|
||||
borderTop,
|
||||
borderBottom,
|
||||
scrollY + targetBlock,
|
||||
scrollY + targetBlock + targetHeight,
|
||||
targetHeight
|
||||
)
|
||||
} else {
|
||||
// block === 'center' is the default
|
||||
blockScroll = targetBlock - viewportHeight / 2
|
||||
}
|
||||
|
||||
if (inline === 'start') {
|
||||
inlineScroll = targetInline
|
||||
} else if (inline === 'center') {
|
||||
inlineScroll = targetInline - viewportWidth / 2
|
||||
} else if (inline === 'end') {
|
||||
inlineScroll = targetInline - viewportWidth
|
||||
} else {
|
||||
// inline === 'nearest' is the default
|
||||
inlineScroll = alignNearest(
|
||||
scrollX,
|
||||
scrollX + viewportWidth,
|
||||
viewportWidth,
|
||||
borderLeft,
|
||||
borderRight,
|
||||
scrollX + targetInline,
|
||||
scrollX + targetInline + targetWidth,
|
||||
targetWidth
|
||||
)
|
||||
}
|
||||
|
||||
// Apply scroll position offsets and ensure they are within bounds
|
||||
// @TODO add more test cases to cover this 100%
|
||||
blockScroll = Math.max(0, blockScroll + scrollY)
|
||||
inlineScroll = Math.max(0, inlineScroll + scrollX)
|
||||
} else {
|
||||
// Handle each scrolling frame that might exist between the target and the viewport
|
||||
if (block === 'start') {
|
||||
blockScroll = targetBlock - top - borderTop
|
||||
} else if (block === 'end') {
|
||||
blockScroll = targetBlock - bottom + borderBottom + scrollbarHeight
|
||||
} else if (block === 'nearest') {
|
||||
blockScroll = alignNearest(
|
||||
top,
|
||||
bottom,
|
||||
height,
|
||||
borderTop,
|
||||
borderBottom + scrollbarHeight,
|
||||
targetBlock,
|
||||
targetBlock + targetHeight,
|
||||
targetHeight
|
||||
)
|
||||
} else {
|
||||
// block === 'center' is the default
|
||||
blockScroll = targetBlock - (top + height / 2) + scrollbarHeight / 2
|
||||
}
|
||||
|
||||
if (inline === 'start') {
|
||||
inlineScroll = targetInline - left - borderLeft
|
||||
} else if (inline === 'center') {
|
||||
inlineScroll = targetInline - (left + width / 2) + scrollbarWidth / 2
|
||||
} else if (inline === 'end') {
|
||||
inlineScroll = targetInline - right + borderRight + scrollbarWidth
|
||||
} else {
|
||||
// inline === 'nearest' is the default
|
||||
inlineScroll = alignNearest(
|
||||
left,
|
||||
right,
|
||||
width,
|
||||
borderLeft,
|
||||
borderRight + scrollbarWidth,
|
||||
targetInline,
|
||||
targetInline + targetWidth,
|
||||
targetWidth
|
||||
)
|
||||
}
|
||||
|
||||
const { scrollLeft, scrollTop } = frame
|
||||
// Ensure scroll coordinates are not out of bounds while applying scroll offsets
|
||||
blockScroll =
|
||||
scaleY === 0
|
||||
? 0
|
||||
: Math.max(
|
||||
0,
|
||||
Math.min(
|
||||
scrollTop + blockScroll / scaleY,
|
||||
frame.scrollHeight - height / scaleY + scrollbarHeight
|
||||
)
|
||||
)
|
||||
inlineScroll =
|
||||
scaleX === 0
|
||||
? 0
|
||||
: Math.max(
|
||||
0,
|
||||
Math.min(
|
||||
scrollLeft + inlineScroll / scaleX,
|
||||
frame.scrollWidth - width / scaleX + scrollbarWidth
|
||||
)
|
||||
)
|
||||
|
||||
// Cache the offset so that parent frames can scroll this into view correctly
|
||||
targetBlock += scrollTop - blockScroll
|
||||
targetInline += scrollLeft - inlineScroll
|
||||
}
|
||||
|
||||
computations.push({ el: frame, top: blockScroll, left: inlineScroll })
|
||||
}
|
||||
|
||||
return computations
|
||||
}
|
||||
Reference in New Issue
Block a user