fix(large-video): Attempt to fix jumping.

When the toolbox is hidden and due to a ReactFocusLock instance the
focus is returned to the toolbox the whole page scrolls to the toolbox
which is positioned outside of the viewport in the bottom.
Then when the animation for displaying the toolbox is started the
scenario looks like the large video is jumping.
Now we don't return the focus from ReactFocusLock to elements which are
not part of the viewport.
pull/13323/head jitsi-meet_8636
Hristo Terezov 2 years ago
parent 4c37ef7a2c
commit 350443ad34
  1. 126
      package-lock.json
  2. 2
      package.json
  3. 12
      react/features/base/popover/components/Popover.web.tsx
  4. 12
      react/features/base/ui/components/web/BaseDialog.tsx
  5. 25
      react/features/base/ui/functions.web.ts
  6. 12
      react/features/toolbox/components/web/Drawer.tsx

126
package-lock.json generated

@ -71,7 +71,7 @@
"react": "17.0.2", "react": "17.0.2",
"react-dom": "17.0.2", "react-dom": "17.0.2",
"react-emoji-render": "1.2.4", "react-emoji-render": "1.2.4",
"react-focus-lock": "2.5.1", "react-focus-lock": "2.9.4",
"react-i18next": "10.11.4", "react-i18next": "10.11.4",
"react-linkify": "1.0.0-alpha", "react-linkify": "1.0.0-alpha",
"react-native": "0.68.6", "react-native": "0.68.6",
@ -10508,9 +10508,9 @@
} }
}, },
"node_modules/focus-lock": { "node_modules/focus-lock": {
"version": "0.9.2", "version": "0.11.6",
"resolved": "https://registry.npmjs.org/focus-lock/-/focus-lock-0.9.2.tgz", "resolved": "https://registry.npmjs.org/focus-lock/-/focus-lock-0.11.6.tgz",
"integrity": "sha512-YtHxjX7a0IC0ZACL5wsX8QdncXofWpGPNoVMuI/nZUrPGp6LmNI6+D5j0pPj+v8Kw5EpweA+T5yImK0rnWf7oQ==", "integrity": "sha512-KSuV3ur4gf2KqMNoZx3nXNVhqCkn42GuTYCX4tXPEwf0MjpFQmNMiN6m7dXaUXgIoivL6/65agoUMg4RLS0Vbg==",
"dependencies": { "dependencies": {
"tslib": "^2.0.3" "tslib": "^2.0.3"
}, },
@ -15415,14 +15415,14 @@
} }
}, },
"node_modules/react-clientside-effect": { "node_modules/react-clientside-effect": {
"version": "1.2.5", "version": "1.2.6",
"resolved": "https://registry.npmjs.org/react-clientside-effect/-/react-clientside-effect-1.2.5.tgz", "resolved": "https://registry.npmjs.org/react-clientside-effect/-/react-clientside-effect-1.2.6.tgz",
"integrity": "sha512-2bL8qFW1TGBHozGGbVeyvnggRpMjibeZM2536AKNENLECutp2yfs44IL8Hmpn8qjFQ2K7A9PnYf3vc7aQq/cPA==", "integrity": "sha512-XGGGRQAKY+q25Lz9a/4EPqom7WRjz3z9R2k4jhVKA/puQFH/5Nt27vFZYql4m4NVNdUvX8PS3O7r/Zzm7cjUlg==",
"dependencies": { "dependencies": {
"@babel/runtime": "^7.12.13" "@babel/runtime": "^7.12.13"
}, },
"peerDependencies": { "peerDependencies": {
"react": "^15.3.0 || ^16.0.0 || ^17.0.0" "react": "^15.3.0 || ^16.0.0 || ^17.0.0 || ^18.0.0"
} }
}, },
"node_modules/react-devtools-core": { "node_modules/react-devtools-core": {
@ -15484,19 +15484,25 @@
} }
}, },
"node_modules/react-focus-lock": { "node_modules/react-focus-lock": {
"version": "2.5.1", "version": "2.9.4",
"resolved": "https://registry.npmjs.org/react-focus-lock/-/react-focus-lock-2.5.1.tgz", "resolved": "https://registry.npmjs.org/react-focus-lock/-/react-focus-lock-2.9.4.tgz",
"integrity": "sha512-gOToRZKVEymGEjFaTRUKgJsdYQrNosoiK7yZnXnnd8bYew4vMzk3Rxb0Q4nyrGwsFuUmgQiSAulQirA0J+v4hA==", "integrity": "sha512-7pEdXyMseqm3kVjhdVH18sovparAzLg5h6WvIx7/Ck3ekjhrrDMEegHSa3swwC8wgfdd7DIdUVRGeiHT9/7Sgg==",
"dependencies": { "dependencies": {
"@babel/runtime": "^7.0.0", "@babel/runtime": "^7.0.0",
"focus-lock": "^0.9.1", "focus-lock": "^0.11.6",
"prop-types": "^15.6.2", "prop-types": "^15.6.2",
"react-clientside-effect": "^1.2.2", "react-clientside-effect": "^1.2.6",
"use-callback-ref": "^1.2.1", "use-callback-ref": "^1.3.0",
"use-sidecar": "^1.0.1" "use-sidecar": "^1.1.2"
}, },
"peerDependencies": { "peerDependencies": {
"react": "^16.8.0 || ^17.0.0" "@types/react": "^16.8.0 || ^17.0.0 || ^18.0.0",
"react": "^16.8.0 || ^17.0.0 || ^18.0.0"
},
"peerDependenciesMeta": {
"@types/react": {
"optional": true
}
} }
}, },
"node_modules/react-freeze": { "node_modules/react-freeze": {
@ -18563,15 +18569,18 @@
} }
}, },
"node_modules/use-callback-ref": { "node_modules/use-callback-ref": {
"version": "1.2.5", "version": "1.3.0",
"resolved": "https://registry.npmjs.org/use-callback-ref/-/use-callback-ref-1.2.5.tgz", "resolved": "https://registry.npmjs.org/use-callback-ref/-/use-callback-ref-1.3.0.tgz",
"integrity": "sha512-gN3vgMISAgacF7sqsLPByqoePooY3n2emTH59Ur5d/M8eg4WTWu1xp8i8DHjohftIyEx0S08RiYxbffr4j8Peg==", "integrity": "sha512-3FT9PRuRdbB9HfXhEq35u4oZkvpJ5kuYbpqhCfmiZyReuRgpnhDlbr2ZEnnuS0RrJAPn6l23xjFg9kpDM+Ms7w==",
"dependencies": {
"tslib": "^2.0.0"
},
"engines": { "engines": {
"node": ">=8.5.0" "node": ">=10"
}, },
"peerDependencies": { "peerDependencies": {
"@types/react": "^16.8.0 || ^17.0.0", "@types/react": "^16.8.0 || ^17.0.0 || ^18.0.0",
"react": "^16.8.0 || ^17.0.0" "react": "^16.8.0 || ^17.0.0 || ^18.0.0"
}, },
"peerDependenciesMeta": { "peerDependenciesMeta": {
"@types/react": { "@types/react": {
@ -18630,25 +18639,26 @@
"integrity": "sha512-HtHatS2U4/h32NlkhupDsPlrbiD27gSH5swBdtXbCAlc6pfOFzaj0FehW/FO12rx8j2Vy4/lJScCiJyM01E+bQ==" "integrity": "sha512-HtHatS2U4/h32NlkhupDsPlrbiD27gSH5swBdtXbCAlc6pfOFzaj0FehW/FO12rx8j2Vy4/lJScCiJyM01E+bQ=="
}, },
"node_modules/use-sidecar": { "node_modules/use-sidecar": {
"version": "1.0.5", "version": "1.1.2",
"resolved": "https://registry.npmjs.org/use-sidecar/-/use-sidecar-1.0.5.tgz", "resolved": "https://registry.npmjs.org/use-sidecar/-/use-sidecar-1.1.2.tgz",
"integrity": "sha512-k9jnrjYNwN6xYLj1iaGhonDghfvmeTmYjAiGvOr7clwKfPjMXJf4/HOr7oT5tJwYafgp2tG2l3eZEOfoELiMcA==", "integrity": "sha512-epTbsLuzZ7lPClpz2TyryBfztm7m+28DlEv2ZCQ3MDr5ssiwyOwGH/e5F9CkfWjJ1t4clvI58yF822/GUkjjhw==",
"dependencies": { "dependencies": {
"detect-node-es": "^1.1.0", "detect-node-es": "^1.1.0",
"tslib": "^1.9.3" "tslib": "^2.0.0"
}, },
"engines": { "engines": {
"node": ">=8.5.0" "node": ">=10"
}, },
"peerDependencies": { "peerDependencies": {
"react": "^16.8.0 || ^17.0.0" "@types/react": "^16.9.0 || ^17.0.0 || ^18.0.0",
"react": "^16.8.0 || ^17.0.0 || ^18.0.0"
},
"peerDependenciesMeta": {
"@types/react": {
"optional": true
}
} }
}, },
"node_modules/use-sidecar/node_modules/tslib": {
"version": "1.14.1",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz",
"integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg=="
},
"node_modules/use-subscription": { "node_modules/use-subscription": {
"version": "1.5.1", "version": "1.5.1",
"resolved": "https://registry.npmjs.org/use-subscription/-/use-subscription-1.5.1.tgz", "resolved": "https://registry.npmjs.org/use-subscription/-/use-subscription-1.5.1.tgz",
@ -27441,9 +27451,9 @@
"integrity": "sha512-1gIBiWJNR0tKUNv8gZuk7l9rVX06OuLzY9AoGio7y/JT4V1IZErEMEq2TJS+PFcw/y0RshZ1J/27VfK1UQzYVg==" "integrity": "sha512-1gIBiWJNR0tKUNv8gZuk7l9rVX06OuLzY9AoGio7y/JT4V1IZErEMEq2TJS+PFcw/y0RshZ1J/27VfK1UQzYVg=="
}, },
"focus-lock": { "focus-lock": {
"version": "0.9.2", "version": "0.11.6",
"resolved": "https://registry.npmjs.org/focus-lock/-/focus-lock-0.9.2.tgz", "resolved": "https://registry.npmjs.org/focus-lock/-/focus-lock-0.11.6.tgz",
"integrity": "sha512-YtHxjX7a0IC0ZACL5wsX8QdncXofWpGPNoVMuI/nZUrPGp6LmNI6+D5j0pPj+v8Kw5EpweA+T5yImK0rnWf7oQ==", "integrity": "sha512-KSuV3ur4gf2KqMNoZx3nXNVhqCkn42GuTYCX4tXPEwf0MjpFQmNMiN6m7dXaUXgIoivL6/65agoUMg4RLS0Vbg==",
"requires": { "requires": {
"tslib": "^2.0.3" "tslib": "^2.0.3"
} }
@ -31161,9 +31171,9 @@
} }
}, },
"react-clientside-effect": { "react-clientside-effect": {
"version": "1.2.5", "version": "1.2.6",
"resolved": "https://registry.npmjs.org/react-clientside-effect/-/react-clientside-effect-1.2.5.tgz", "resolved": "https://registry.npmjs.org/react-clientside-effect/-/react-clientside-effect-1.2.6.tgz",
"integrity": "sha512-2bL8qFW1TGBHozGGbVeyvnggRpMjibeZM2536AKNENLECutp2yfs44IL8Hmpn8qjFQ2K7A9PnYf3vc7aQq/cPA==", "integrity": "sha512-XGGGRQAKY+q25Lz9a/4EPqom7WRjz3z9R2k4jhVKA/puQFH/5Nt27vFZYql4m4NVNdUvX8PS3O7r/Zzm7cjUlg==",
"requires": { "requires": {
"@babel/runtime": "^7.12.13" "@babel/runtime": "^7.12.13"
} }
@ -31207,16 +31217,16 @@
} }
}, },
"react-focus-lock": { "react-focus-lock": {
"version": "2.5.1", "version": "2.9.4",
"resolved": "https://registry.npmjs.org/react-focus-lock/-/react-focus-lock-2.5.1.tgz", "resolved": "https://registry.npmjs.org/react-focus-lock/-/react-focus-lock-2.9.4.tgz",
"integrity": "sha512-gOToRZKVEymGEjFaTRUKgJsdYQrNosoiK7yZnXnnd8bYew4vMzk3Rxb0Q4nyrGwsFuUmgQiSAulQirA0J+v4hA==", "integrity": "sha512-7pEdXyMseqm3kVjhdVH18sovparAzLg5h6WvIx7/Ck3ekjhrrDMEegHSa3swwC8wgfdd7DIdUVRGeiHT9/7Sgg==",
"requires": { "requires": {
"@babel/runtime": "^7.0.0", "@babel/runtime": "^7.0.0",
"focus-lock": "^0.9.1", "focus-lock": "^0.11.6",
"prop-types": "^15.6.2", "prop-types": "^15.6.2",
"react-clientside-effect": "^1.2.2", "react-clientside-effect": "^1.2.6",
"use-callback-ref": "^1.2.1", "use-callback-ref": "^1.3.0",
"use-sidecar": "^1.0.1" "use-sidecar": "^1.1.2"
} }
}, },
"react-freeze": { "react-freeze": {
@ -33521,9 +33531,12 @@
"integrity": "sha512-cwESVXlO3url9YWlFW/TA9cshCEhtu7IKJ/p5soJ/gGpj7vbvFrAY/eIioQ6Dw23KjZhYgiIo8HOs1nQ2vr/oQ==" "integrity": "sha512-cwESVXlO3url9YWlFW/TA9cshCEhtu7IKJ/p5soJ/gGpj7vbvFrAY/eIioQ6Dw23KjZhYgiIo8HOs1nQ2vr/oQ=="
}, },
"use-callback-ref": { "use-callback-ref": {
"version": "1.2.5", "version": "1.3.0",
"resolved": "https://registry.npmjs.org/use-callback-ref/-/use-callback-ref-1.2.5.tgz", "resolved": "https://registry.npmjs.org/use-callback-ref/-/use-callback-ref-1.3.0.tgz",
"integrity": "sha512-gN3vgMISAgacF7sqsLPByqoePooY3n2emTH59Ur5d/M8eg4WTWu1xp8i8DHjohftIyEx0S08RiYxbffr4j8Peg==" "integrity": "sha512-3FT9PRuRdbB9HfXhEq35u4oZkvpJ5kuYbpqhCfmiZyReuRgpnhDlbr2ZEnnuS0RrJAPn6l23xjFg9kpDM+Ms7w==",
"requires": {
"tslib": "^2.0.0"
}
}, },
"use-composed-ref": { "use-composed-ref": {
"version": "1.2.1", "version": "1.2.1",
@ -33554,19 +33567,12 @@
"integrity": "sha512-HtHatS2U4/h32NlkhupDsPlrbiD27gSH5swBdtXbCAlc6pfOFzaj0FehW/FO12rx8j2Vy4/lJScCiJyM01E+bQ==" "integrity": "sha512-HtHatS2U4/h32NlkhupDsPlrbiD27gSH5swBdtXbCAlc6pfOFzaj0FehW/FO12rx8j2Vy4/lJScCiJyM01E+bQ=="
}, },
"use-sidecar": { "use-sidecar": {
"version": "1.0.5", "version": "1.1.2",
"resolved": "https://registry.npmjs.org/use-sidecar/-/use-sidecar-1.0.5.tgz", "resolved": "https://registry.npmjs.org/use-sidecar/-/use-sidecar-1.1.2.tgz",
"integrity": "sha512-k9jnrjYNwN6xYLj1iaGhonDghfvmeTmYjAiGvOr7clwKfPjMXJf4/HOr7oT5tJwYafgp2tG2l3eZEOfoELiMcA==", "integrity": "sha512-epTbsLuzZ7lPClpz2TyryBfztm7m+28DlEv2ZCQ3MDr5ssiwyOwGH/e5F9CkfWjJ1t4clvI58yF822/GUkjjhw==",
"requires": { "requires": {
"detect-node-es": "^1.1.0", "detect-node-es": "^1.1.0",
"tslib": "^1.9.3" "tslib": "^2.0.0"
},
"dependencies": {
"tslib": {
"version": "1.14.1",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz",
"integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg=="
}
} }
}, },
"use-subscription": { "use-subscription": {

@ -76,7 +76,7 @@
"react": "17.0.2", "react": "17.0.2",
"react-dom": "17.0.2", "react-dom": "17.0.2",
"react-emoji-render": "1.2.4", "react-emoji-render": "1.2.4",
"react-focus-lock": "2.5.1", "react-focus-lock": "2.9.4",
"react-i18next": "10.11.4", "react-i18next": "10.11.4",
"react-linkify": "1.0.0-alpha", "react-linkify": "1.0.0-alpha",
"react-native": "0.68.6", "react-native": "0.68.6",

@ -6,6 +6,7 @@ import { IReduxState } from '../../../app/types';
import DialogPortal from '../../../toolbox/components/web/DialogPortal'; import DialogPortal from '../../../toolbox/components/web/DialogPortal';
import Drawer from '../../../toolbox/components/web/Drawer'; import Drawer from '../../../toolbox/components/web/Drawer';
import JitsiPortal from '../../../toolbox/components/web/JitsiPortal'; import JitsiPortal from '../../../toolbox/components/web/JitsiPortal';
import { isElementInTheViewport } from '../../ui/functions.web';
import { getContextMenuStyle } from '../functions.web'; import { getContextMenuStyle } from '../functions.web';
/** /**
@ -258,7 +259,16 @@ class Popover extends Component<IProps, IState> {
'aria-labelledby': headingId, 'aria-labelledby': headingId,
'aria-label': !headingId && headingLabel ? headingLabel : undefined 'aria-label': !headingId && headingLabel ? headingLabel : undefined
}} }}
returnFocus = { true }> returnFocus = {
// If we return the focus to an element outside the viewport the page will scroll to
// this element which in our case is undesirable and the element is outside of the
// viewport on purpose (to be hidden). For example if we return the focus to the toolbox
// when it is hidden the whole page will move up in order to show the toolbox. This is
// usually followed up with displaying the toolbox (because now it is on focus) but
// because of the animation the whole scenario looks like jumping large video.
isElementInTheViewport
}>
{this._renderContent()} {this._renderContent()}
</ReactFocusLock> </ReactFocusLock>
</DialogPortal> </DialogPortal>

@ -5,6 +5,7 @@ import { keyframes } from 'tss-react';
import { makeStyles } from 'tss-react/mui'; import { makeStyles } from 'tss-react/mui';
import { withPixelLineHeight } from '../../../styles/functions.web'; import { withPixelLineHeight } from '../../../styles/functions.web';
import { isElementInTheViewport } from '../../functions.web';
import { DialogTransitionContext } from './DialogTransition'; import { DialogTransitionContext } from './DialogTransition';
@ -184,7 +185,16 @@ const BaseDialog = ({
onClick = { onBackdropClick } /> onClick = { onBackdropClick } />
<FocusLock <FocusLock
className = { classes.focusLock } className = { classes.focusLock }
returnFocus = { true }> returnFocus = {
// If we return the focus to an element outside the viewport the page will scroll to
// this element which in our case is undesirable and the element is outside of the
// viewport on purpose (to be hidden). For example if we return the focus to the toolbox
// when it is hidden the whole page will move up in order to show the toolbox. This is
// usually followed up with displaying the toolbox (because now it is on focus) but
// because of the animation the whole scenario looks like jumping large video.
isElementInTheViewport
}>
<div <div
aria-describedby = { description } aria-describedby = { description }
aria-labelledby = { title ?? t(titleKey ?? '') } aria-labelledby = { title ?? t(titleKey ?? '') }

@ -57,3 +57,28 @@ export const findAncestorByClass = (target: HTMLElement | null, cssClass: string
return findAncestorByClass(target.parentElement, cssClass); return findAncestorByClass(target.parentElement, cssClass);
}; };
/**
* Checks if the passed element is visible in the viewport.
*
* @param {Element} element - The element.
* @returns {boolean}
*/
export function isElementInTheViewport(element?: Element): boolean {
if (!element) {
return false;
}
if (!document.body.contains(element)) {
return false;
}
const { innerHeight, innerWidth } = window;
const { bottom, left, right, top } = element.getBoundingClientRect();
if (bottom <= innerHeight && top >= 0 && left >= 0 && right <= innerWidth) {
return true;
}
return false;
}

@ -2,6 +2,7 @@ import React, { KeyboardEvent, ReactNode, useCallback } from 'react';
import ReactFocusLock from 'react-focus-lock'; import ReactFocusLock from 'react-focus-lock';
import { makeStyles } from 'tss-react/mui'; import { makeStyles } from 'tss-react/mui';
import { isElementInTheViewport } from '../../../base/ui/functions.web';
import { DRAWER_MAX_HEIGHT } from '../../constants'; import { DRAWER_MAX_HEIGHT } from '../../constants';
@ -107,7 +108,16 @@ function Drawer({
'aria-modal': true, 'aria-modal': true,
'aria-labelledby': `#${headingId}` 'aria-labelledby': `#${headingId}`
}} }}
returnFocus = { true }> returnFocus = {
// If we return the focus to an element outside the viewport the page will scroll to
// this element which in our case is undesirable and the element is outside of the
// viewport on purpose (to be hidden). For example if we return the focus to the toolbox
// when it is hidden the whole page will move up in order to show the toolbox. This is
// usually followed up with displaying the toolbox (because now it is on focus) but
// because of the animation the whole scenario looks like jumping large video.
isElementInTheViewport
}>
{children} {children}
</ReactFocusLock> </ReactFocusLock>
</div> </div>

Loading…
Cancel
Save