change preview card dropdown position depending on viewport (#14473)
This commit is contained in:
parent
443e852091
commit
5efd72040b
5 changed files with 84 additions and 3 deletions
|
|
@ -29,4 +29,10 @@
|
|||
top: 0;
|
||||
}
|
||||
}
|
||||
|
||||
&.reverse {
|
||||
// Flips the dropdown to drop-upwards when set
|
||||
bottom: 100%;
|
||||
top: unset;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,7 +3,11 @@ import ahoy from 'ahoy.js';
|
|||
import { Snackbar, addSnackbarItem } from '../Snackbar';
|
||||
import { addFullScreenModeControl } from '../utilities/codeFullscreenModeSwitcher';
|
||||
import { embedGists } from '../utilities/gist';
|
||||
import { initializeDropdown } from '@utilities/dropdownUtils';
|
||||
import {
|
||||
initializeDropdown,
|
||||
getDropdownRepositionListener,
|
||||
} from '../utilities/dropdownUtils';
|
||||
import { getInstantClick } from '../topNavigation/utilities';
|
||||
|
||||
/* global Runtime */
|
||||
|
||||
|
|
@ -188,3 +192,18 @@ if (profilePreviewTrigger?.dataset.initialized !== 'true') {
|
|||
|
||||
const targetNode = document.querySelector('#comments');
|
||||
targetNode && embedGists(targetNode);
|
||||
|
||||
// Preview card dropdowns reposition on scroll
|
||||
const dropdownRepositionListener = getDropdownRepositionListener();
|
||||
|
||||
document.addEventListener('scroll', dropdownRepositionListener);
|
||||
|
||||
getInstantClick().then((ic) => {
|
||||
ic.on('change', () => {
|
||||
document.removeEventListener('scroll', dropdownRepositionListener);
|
||||
});
|
||||
});
|
||||
|
||||
window.addEventListener('beforeunload', () => {
|
||||
document.removeEventListener('scroll', dropdownRepositionListener);
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,5 +1,8 @@
|
|||
import { addSnackbarItem } from '../Snackbar';
|
||||
import { initializeDropdown } from '@utilities/dropdownUtils';
|
||||
import {
|
||||
initializeDropdown,
|
||||
getDropdownRepositionListener,
|
||||
} from '@utilities/dropdownUtils';
|
||||
|
||||
/* global Runtime */
|
||||
|
||||
|
|
@ -122,12 +125,18 @@ observer.observe(document.getElementById('comment-trees-container'), {
|
|||
subtree: true,
|
||||
});
|
||||
|
||||
// Preview card dropdowns reposition on scroll
|
||||
const dropdownRepositionListener = getDropdownRepositionListener();
|
||||
document.addEventListener('scroll', dropdownRepositionListener);
|
||||
|
||||
InstantClick.on('change', () => {
|
||||
observer.disconnect();
|
||||
document.removeEventListener('scroll', dropdownRepositionListener);
|
||||
});
|
||||
|
||||
window.addEventListener('beforeunload', () => {
|
||||
observer.disconnect();
|
||||
document.removeEventListener('scroll', dropdownRepositionListener);
|
||||
});
|
||||
|
||||
initializeArticlePageDropdowns();
|
||||
|
|
|
|||
|
|
@ -1,3 +1,50 @@
|
|||
import { isInViewport } from '@utilities/viewport';
|
||||
import { debounceAction } from '@utilities/debounceAction';
|
||||
|
||||
/**
|
||||
* Helper function designed to be used on scroll to detect when dropdowns should switch from dropping downwards/upwards.
|
||||
* The action is debounced since scroll events are usually fired several at a time.
|
||||
*
|
||||
* @returns {Function} a debounced function that handles the repositioning of dropdowns
|
||||
* @example
|
||||
*
|
||||
* document.addEventListener('scroll', getDropdownRepositionListener());
|
||||
*/
|
||||
export const getDropdownRepositionListener = () =>
|
||||
debounceAction(handleDropdownRepositions);
|
||||
|
||||
/**
|
||||
* Checks for all dropdowns on the page which have the attribute 'data-repositioning-dropdown', signalling
|
||||
* they should dynamically change between dropping downwards or upwards, depending on viewport position.
|
||||
*
|
||||
* Any dropdowns not fully in view when dropping down will be switched to dropping upwards.
|
||||
*/
|
||||
const handleDropdownRepositions = () => {
|
||||
// Select all of the dropdowns which should reposition
|
||||
const allRepositioningDropdowns = document.querySelectorAll(
|
||||
'[data-repositioning-dropdown]',
|
||||
);
|
||||
|
||||
for (const element of allRepositioningDropdowns) {
|
||||
// Default to dropping downwards
|
||||
element.classList.remove('reverse');
|
||||
|
||||
// We can't determine position on an element with display:none, so we "show" the dropdown with 0 opacity very temporarily
|
||||
element.style.opacity = 0;
|
||||
element.style.display = 'block';
|
||||
const isWithinViewport = isInViewport({ element });
|
||||
|
||||
// Revert the temporary changes to determine position
|
||||
element.style.removeProperty('display');
|
||||
element.style.removeProperty('opacity');
|
||||
|
||||
if (!isWithinViewport) {
|
||||
// If the element isn't fully visible when dropping down, reverse the direction
|
||||
element.classList.add('reverse');
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Helper query string to identify interactive/focusable HTML elements
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
<div id="<%= id %>" class="profile-preview-card__content crayons-dropdown" style="--card-color: <%= Color::CompareHex.new([user_colors(actor)[:bg], user_colors(actor)[:text]]).brightness(0.88) %>; border-top: var(--su-7) solid var(--card-color);" data-testid="profile-preview-card">
|
||||
<div id="<%= id %>" class="profile-preview-card__content crayons-dropdown" style="--card-color: <%= Color::CompareHex.new([user_colors(actor)[:bg], user_colors(actor)[:text]]).brightness(0.88) %>; border-top: var(--su-7) solid var(--card-color);" data-testid="profile-preview-card" data-repositioning-dropdown="true">
|
||||
<div class="gap-4 grid">
|
||||
<%= render "shared/profile_card_content", context: "preview-card", actor: actor %>
|
||||
</div>
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue